From 3fa80a3ea0a3d4a0efae57268deab46eb568f781 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Tue, 30 Aug 2011 02:09:56 -0400 Subject: [PATCH] Backed out changeset 33031c875984. Bug 90268. r=josh --- content/base/public/nsContentUtils.h | 7 - .../base/public/nsIObjectLoadingContent.idl | 26 +- content/base/src/nsContentUtils.cpp | 31 +- content/base/src/nsDocument.cpp | 6 - content/base/src/nsNodeUtils.cpp | 10 +- content/base/src/nsObjectLoadingContent.cpp | 819 ++++++++---------- content/base/src/nsObjectLoadingContent.h | 43 +- content/html/document/src/PluginDocument.cpp | 20 +- dom/base/nsDOMClassInfo.cpp | 20 +- dom/plugins/base/nsNPAPIPluginInstance.cpp | 7 + dom/plugins/base/nsNPAPIPluginInstance.h | 3 + dom/plugins/base/nsPluginHost.cpp | 6 + dom/plugins/base/nsPluginHost.h | 1 + dom/plugins/base/nsPluginInstanceOwner.cpp | 350 ++++---- dom/plugins/base/nsPluginInstanceOwner.h | 34 +- dom/plugins/test/mochitest/Makefile.in | 6 - .../test/mochitest/test_display_none.html | 37 - .../test_instance_re-parent-windowed.html | 59 -- .../mochitest/test_instance_re-parent.html | 59 -- .../mochitest/test_instance_unparent1.html | 41 - .../mochitest/test_instance_unparent2.html | 50 -- .../mochitest/test_instance_unparent3.html | 44 - layout/base/nsPresShell.cpp | 14 +- layout/generic/nsIObjectFrame.h | 36 + layout/generic/nsObjectFrame.cpp | 621 +++++++++++-- layout/generic/nsObjectFrame.h | 64 +- layout/generic/nsPluginUtilsOSX.mm | 7 +- 27 files changed, 1262 insertions(+), 1159 deletions(-) delete mode 100644 dom/plugins/test/mochitest/test_display_none.html delete mode 100644 dom/plugins/test/mochitest/test_instance_re-parent-windowed.html delete mode 100644 dom/plugins/test/mochitest/test_instance_re-parent.html delete mode 100644 dom/plugins/test/mochitest/test_instance_unparent1.html delete mode 100644 dom/plugins/test/mochitest/test_instance_unparent2.html delete mode 100644 dom/plugins/test/mochitest/test_instance_unparent3.html diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 165438ee524a..0fc672e12160 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1662,13 +1662,6 @@ public: const nsAString& aClasses, nsIDOMNodeList** aReturn); - /** - * Returns the widget for this document if there is one. Looks at all ancestor - * documents to try to find a widget, so for example this can still find a - * widget for documents in display:none frames that have no presentation. - */ - static nsIWidget *WidgetForDocument(nsIDocument *aDoc); - /** * Returns a layer manager to use for the given document. Basically we * look up the document hierarchy for the first document which has diff --git a/content/base/public/nsIObjectLoadingContent.idl b/content/base/public/nsIObjectLoadingContent.idl index 609c52cb6a88..d1aba4945fa2 100644 --- a/content/base/public/nsIObjectLoadingContent.idl +++ b/content/base/public/nsIObjectLoadingContent.idl @@ -51,7 +51,7 @@ interface nsIDOMClientRect; /** * This interface represents a content node that loads objects. */ -[scriptable, uuid(6D8914C7-0E22-4452-8962-11B69BBE84D7)] +[scriptable, uuid(107e8048-d00f-4711-bd21-97184ccae0b1)] interface nsIObjectLoadingContent : nsISupports { const unsigned long TYPE_LOADING = 0; @@ -86,6 +86,24 @@ interface nsIObjectLoadingContent : nsISupports */ [noscript] readonly attribute nsNPAPIPluginInstancePtr 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 + * 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] nsNPAPIPluginInstancePtr ensureInstantiation(); + /** * Tells the content about an associated object frame. * This can be called multiple times for different frames. @@ -95,8 +113,6 @@ interface nsIObjectLoadingContent : nsISupports */ [noscript] void hasNewFrame(in nsIObjectFrame aFrame); - [noscript] void disconnectFrame(); - /** * If this object is in going to be printed, this method * returns the nsIObjectFrame object which should be used when @@ -109,8 +125,4 @@ interface nsIObjectLoadingContent : nsISupports in AString pluginDumpID, in AString browserDumpID, in boolean submittedCrashReport); - - [noscript] void stopPluginInstance(); - - [noscript] void startPluginInstance(); }; diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index ab9fa008bfc1..1f48a5e59d39 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -5516,8 +5516,9 @@ nsContentUtils::PlatformToDOMLineBreaks(nsString &aString) } } -nsIWidget * -nsContentUtils::WidgetForDocument(nsIDocument *aDoc) +static already_AddRefed +LayerManagerForDocumentInternal(nsIDocument *aDoc, bool aRequirePersistent, + bool* aAllowRetaining) { nsIDocument* doc = aDoc; nsIDocument* displayDoc = doc->GetDisplayDocument(); @@ -5552,7 +5553,15 @@ nsContentUtils::WidgetForDocument(nsIDocument *aDoc) if (rootView) { nsIView* displayRoot = nsIViewManager::GetDisplayRootFor(rootView); if (displayRoot) { - return displayRoot->GetNearestWidget(nsnull); + nsIWidget* widget = displayRoot->GetNearestWidget(nsnull); + if (widget) { + nsRefPtr manager = + widget-> + GetLayerManager(aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT : + nsIWidget::LAYER_MANAGER_CURRENT, + aAllowRetaining); + return manager.forget(); + } } } } @@ -5561,22 +5570,6 @@ nsContentUtils::WidgetForDocument(nsIDocument *aDoc) return nsnull; } -static already_AddRefed -LayerManagerForDocumentInternal(nsIDocument *aDoc, bool aRequirePersistent, - bool* aAllowRetaining) -{ - nsIWidget *widget = nsContentUtils::WidgetForDocument(aDoc); - if (widget) { - nsRefPtr manager = - widget->GetLayerManager(aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT : - nsIWidget::LAYER_MANAGER_CURRENT, - aAllowRetaining); - return manager.forget(); - } - - return nsnull; -} - already_AddRefed nsContentUtils::LayerManagerForDocument(nsIDocument *aDoc, bool *aAllowRetaining) { diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 65c80e0adc2f..392670328b47 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -173,7 +173,6 @@ #include "nsIDOMPageTransitionEvent.h" #include "nsFrameLoader.h" #include "nsEscape.h" -#include "nsObjectLoadingContent.h" #ifdef MOZ_MEDIA #include "nsHTMLMediaElement.h" #endif // MOZ_MEDIA @@ -3748,11 +3747,6 @@ NotifyActivityChanged(nsIContent *aContent, void *aUnused) mediaElem->NotifyOwnerDocumentActivityChanged(); } #endif - nsCOMPtr objectLoadingContent(do_QueryInterface(aContent)); - if (objectLoadingContent) { - nsObjectLoadingContent* olc = static_cast(objectLoadingContent.get()); - olc->NotifyOwnerDocumentActivityChanged(); - } } void diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index c54c709f9b7a..f75e08f1cc40 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -66,7 +66,6 @@ #include "jsobj.h" #include "jsgc.h" #include "xpcpublic.h" -#include "nsObjectLoadingContent.h" using namespace mozilla::dom; @@ -576,20 +575,15 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep, } } - if (wasRegistered && oldDoc != newDoc) { #ifdef MOZ_MEDIA + if (wasRegistered && oldDoc != newDoc) { nsCOMPtr domMediaElem(do_QueryInterface(aNode)); if (domMediaElem) { nsHTMLMediaElement* mediaElem = static_cast(aNode); mediaElem->NotifyOwnerDocumentActivityChanged(); } -#endif - nsCOMPtr objectLoadingContent(do_QueryInterface(aNode)); - if (objectLoadingContent) { - nsObjectLoadingContent* olc = static_cast(objectLoadingContent.get()); - olc->NotifyOwnerDocumentActivityChanged(); - } } +#endif // nsImageLoadingContent needs to know when its document changes if (oldDoc != newDoc) { diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 51fdc81cb96f..04edb49d449e 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -69,7 +69,6 @@ #include "nsIScriptChannel.h" #include "nsIBlocklistService.h" #include "nsIAsyncVerifyRedirectCallback.h" -#include "nsIAppShell.h" #include "nsPluginError.h" @@ -98,12 +97,6 @@ #include "nsIChannelPolicy.h" #include "nsChannelPolicy.h" #include "mozilla/dom/Element.h" -#include "nsObjectFrame.h" -#include "nsDOMClassInfo.h" - -#include "nsWidgetsCID.h" -#include "nsContentCID.h" -static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #ifdef PR_LOGGING static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc"); @@ -114,14 +107,18 @@ static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc"); class nsAsyncInstantiateEvent : public nsRunnable { public: + // This stores both the content and the frame so that Instantiate calls can be + // avoided if the frame changed in the meantime. nsObjectLoadingContent *mContent; + nsWeakFrame mFrame; nsCString mContentType; nsCOMPtr mURI; nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent, + nsIFrame* aFrame, const nsCString& aType, nsIURI* aURI) - : mContent(aContent), mContentType(aType), mURI(aURI) + : mContent(aContent), mFrame(aFrame), mContentType(aType), mURI(aURI) { static_cast(mContent)->AddRef(); } @@ -137,51 +134,44 @@ public: NS_IMETHODIMP nsAsyncInstantiateEvent::Run() { - // do nothing if we've been revoked - if (mContent->mPendingInstantiateEvent != this) { + // Check if we've been "revoked" + if (mContent->mPendingInstantiateEvent != this) return NS_OK; - } mContent->mPendingInstantiateEvent = nsnull; - if (LOG_ENABLED()) { - nsCAutoString spec; - if (mURI) { - mURI->GetSpec(spec); + // Make sure that we still have the right frame (NOTE: we don't need to check + // 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-> + GetExistingFrame(nsObjectLoadingContent::eFlushContent); + + nsIFrame* objectFrame = nsnull; + if (frame) { + objectFrame = do_QueryFrame(frame); + } + + if (objectFrame && + mFrame.GetFrame() == objectFrame && + mContent->mURI == mURI && + mContent->mContentType.Equals(mContentType)) { + if (LOG_ENABLED()) { + nsCAutoString spec; + if (mURI) { + mURI->GetSpec(spec); + } + LOG(("OBJLC [%p]: Handling Instantiate event: Type=<%s> URI=%p<%s>\n", + mContent, mContentType.get(), mURI.get(), spec.get())); } - LOG(("OBJLC [%p]: Handling Instantiate event: Type=<%s> URI=%p<%s>\n", - mContent, mContentType.get(), mURI.get(), spec.get())); - } - return mContent->InstantiatePluginInstance(mContentType.get(), mURI.get()); -} - -// Checks to see if the content for a plugin instance has a parent. -// The plugin instance is stopped if there is no parent. -class InDocCheckEvent : public nsRunnable { -public: - nsCOMPtr mContent; - - InDocCheckEvent(nsIContent* aContent) - : mContent(aContent) - { - } - - ~InDocCheckEvent() - { - } - - NS_IMETHOD Run(); -}; - -NS_IMETHODIMP -InDocCheckEvent::Run() -{ - if (!mContent->IsInDoc()) { - nsCOMPtr olc = do_QueryInterface(mContent); - if (olc) { - olc->StopPluginInstance(); + nsresult rv = mContent->Instantiate(frame, mContentType, mURI); + if (NS_FAILED(rv)) { + mContent->Fallback(PR_TRUE); } + } else { + LOG(("OBJLC [%p]: Discarding event, data changed\n", mContent)); } + return NS_OK; } @@ -340,69 +330,6 @@ nsPluginCrashedEvent::Run() return NS_OK; } -class nsStopPluginRunnable : public nsRunnable, public nsITimerCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - - nsStopPluginRunnable(nsPluginInstanceOwner *aInstanceOwner) - : mInstanceOwner(aInstanceOwner) - { - NS_ASSERTION(aInstanceOwner, "need an owner"); - } - - // nsRunnable - NS_IMETHOD Run(); - - // nsITimerCallback - NS_IMETHOD Notify(nsITimer *timer); - -private: - nsCOMPtr mTimer; - nsRefPtr mInstanceOwner; -}; - -NS_IMPL_ISUPPORTS_INHERITED1(nsStopPluginRunnable, nsRunnable, nsITimerCallback) - -NS_IMETHODIMP -nsStopPluginRunnable::Notify(nsITimer *aTimer) -{ - return Run(); -} - -NS_IMETHODIMP -nsStopPluginRunnable::Run() -{ - // InitWithCallback calls Release before AddRef so we need to hold a - // strong ref on 'this' since we fall through to this scope if it fails. - nsCOMPtr kungFuDeathGrip = this; - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - PRUint32 currentLevel = 0; - appShell->GetEventloopNestingLevel(¤tLevel); - if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) { - if (!mTimer) - mTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (mTimer) { - // Fire 100ms timer to try to tear down this plugin as quickly as - // possible once the nesting level comes back down. - nsresult rv = mTimer->InitWithCallback(this, 100, nsITimer::TYPE_ONE_SHOT); - if (NS_SUCCEEDED(rv)) { - return rv; - } - } - NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe " - "time). Stopping the plugin now, this might crash."); - } - } - - mTimer = nsnull; - - nsObjectLoadingContent::DoStopPlugin(mInstanceOwner, PR_FALSE); - - return NS_OK; -} - class AutoNotifier { public: AutoNotifier(nsObjectLoadingContent* aContent, PRBool aNotify) : @@ -569,153 +496,6 @@ nsObjectLoadingContent::~nsObjectLoadingContent() } } -nsresult -nsObjectLoadingContent::InstantiatePluginInstance(nsIChannel* aChannel, nsIStreamListener** aStreamListener) -{ - if (mInstanceOwner) { - return NS_OK; - } - - mInstanceOwner = new nsPluginInstanceOwner(); - if (!mInstanceOwner) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsObjectFrame* objectFrame = GetExistingFrame(eFlushLayout); - - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - nsresult rv = mInstanceOwner->Init(objectFrame, thisContent); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv)); - nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); - NS_ENSURE_SUCCESS(rv, rv); - - if (objectFrame) { - nsWeakFrame weakFrame(objectFrame); - - objectFrame->SetInstanceOwner(mInstanceOwner.get()); - - // This must be done before instantiating the plugin instance - objectFrame->FixupWindow(objectFrame->GetContentRectRelativeToSelf().Size()); - if (weakFrame.IsAlive()) { - // Ensure we redraw when a plugin instance is instantiated - objectFrame->Invalidate(objectFrame->GetContentRectRelativeToSelf()); - } - } - - return pluginHost->InstantiatePluginForChannel(aChannel, mInstanceOwner, aStreamListener); -} - -nsresult -nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI) -{ - if (mInstanceOwner) { - return NS_OK; - } - - nsCString typeToUse(aMimeType); - if (typeToUse.IsEmpty() && aURI) { - IsPluginEnabledByExtension(aURI, typeToUse); - } - - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - - nsCOMPtr baseURI; - if (!aURI) { - // We need some URI. If we have nothing else, use the base URI. - // XXX(biesi): The code used to do this. Not sure why this is correct... - GetObjectBaseURI(thisContent, getter_AddRefs(baseURI)); - aURI = baseURI; - } - - mInstanceOwner = new nsPluginInstanceOwner(); - if (!mInstanceOwner) - return NS_ERROR_OUT_OF_MEMORY; - - nsObjectFrame *objectFrame = GetExistingFrame(eFlushLayout); - - nsresult rv = mInstanceOwner->Init(objectFrame, thisContent); - NS_ENSURE_SUCCESS(rv, rv); - - // get the nsIPluginHost service - nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv)); - nsPluginHost* pluginHost = static_cast(pluginHostCOM.get()); - if (NS_FAILED(rv)) - return rv; - - // If you add early return(s), be sure to balance this call to - // appShell->SuspendNative() with additional call(s) to - // appShell->ReturnNative(). - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - appShell->SuspendNative(); - } - - nsCOMPtr pDoc(do_QueryInterface(thisContent->GetCurrentDoc())); - PRBool fullPageMode = PR_FALSE; - if (pDoc) { - pDoc->GetWillHandleInstantiation(&fullPageMode); - } - - if (fullPageMode) { - nsCOMPtr stream; - rv = pluginHost->InstantiateFullPagePlugin(aMimeType, aURI, mInstanceOwner, getter_AddRefs(stream)); - if (NS_SUCCEEDED(rv)) { - pDoc->SetStreamListener(stream); - } - } else { /* embedded mode */ - rv = pluginHost->InstantiateEmbeddedPlugin(aMimeType, aURI, mInstanceOwner); - } - - // Note that |this| may very well be destroyed already! - - if (appShell) { - appShell->ResumeNative(); - } - - // Set up scripting interfaces. - NotifyContentObjectWrapper(); - - // This is necessary here for some reason. - if (NS_SUCCEEDED(rv) && objectFrame) { - objectFrame->CallSetWindow(); - } - - nsRefPtr pluginInstance; - GetPluginInstance(getter_AddRefs(pluginInstance)); - if (pluginInstance) { - nsCOMPtr pluginTag; - pluginHost->GetPluginTagForInstance(pluginInstance, getter_AddRefs(pluginTag)); - - nsCOMPtr blocklist = - do_GetService("@mozilla.org/extensions/blocklist;1"); - if (blocklist) { - PRUint32 blockState = nsIBlocklistService::STATE_NOT_BLOCKED; - blocklist->GetPluginBlocklistState(pluginTag, EmptyString(), - EmptyString(), &blockState); - if (blockState == nsIBlocklistService::STATE_OUTDATED) - FirePluginError(thisContent, ePluginOutdated); - } - } - - return rv; -} - -void -nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged() -{ - if (!mInstanceOwner) { - return; - } - - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - nsIDocument* ownerDoc = thisContent->GetOwnerDoc(); - if (ownerDoc && !ownerDoc->IsActive()) { - StopPluginInstance(); - } -} - // nsIRequestObserver NS_IMETHODIMP nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, @@ -752,11 +532,13 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, chan->SetContentType(channelType); } - // We want to use the channel type unless one of the following is true: + // We want to use the channel type unless one of the following is + // true: // // 1) The channel type is application/octet-stream and we have a // type hint and the type hint is not a document type. // 2) Our type hint is a type that we support with a plugin. + if ((channelType.EqualsASCII(APPLICATION_OCTET_STREAM) && !mContentType.IsEmpty() && GetTypeOfContent(mContentType) != eType_Document) || @@ -768,6 +550,7 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, // Set the type we'll use for dispatch on the channel. Otherwise we could // end up trying to dispatch to a nsFrameLoader, which will complain that // it couldn't find a way to handle application/octet-stream + nsCAutoString typeHint, dummy; NS_ParseContentType(mContentType, typeHint, dummy); if (!typeHint.IsEmpty()) { @@ -904,13 +687,42 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, break; } case eType_Plugin: - mInstantiating = PR_TRUE; - rv = InstantiatePluginInstance(chan, getter_AddRefs(mFinalListener)); + 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 = 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; + } + + { + nsIFrame *nsiframe = do_QueryFrame(frame); + + nsWeakFrame weakFrame(nsiframe); + + rv = frame->Instantiate(chan, getter_AddRefs(mFinalListener)); + + mInstantiating = PR_FALSE; + + if (!weakFrame.IsAlive()) { + // The frame was destroyed while instantiating. Abort the load. + return NS_BINDING_ABORTED; + } + } + break; case eType_Loading: NS_NOTREACHED("Should not have a loading type here!"); case eType_Null: + LOG(("OBJLC [%p]: Unsupported type, falling back\n", this)); // Need to fallback here (instead of using the case below), so that we can // set mFallbackReason without it being overwritten. This is also why we // return early. @@ -930,21 +742,31 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, mType = newType; rv = mFinalListener->OnStartRequest(aRequest, aContext); if (NS_FAILED(rv)) { + LOG(("OBJLC [%p]: mFinalListener->OnStartRequest failed (%08x), falling back\n", + this, rv)); #ifdef XP_MACOSX // Shockwave on Mac is special and returns an error here even when it // handles the content 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) { - TryNotifyContentObjectWrapper(); + 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; } + LOG(("OBJLC [%p]: Found no listener, falling back\n", this)); Fallback(PR_FALSE); return NS_BINDING_ABORTED; } @@ -1024,36 +846,150 @@ nsObjectLoadingContent::GetDisplayedType(PRUint32* aType) return NS_OK; } + NS_IMETHODIMP -nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) +nsObjectLoadingContent::EnsureInstantiation(nsNPAPIPluginInstance** aInstance) { - // Not having an instance yet is OK. - if (!mInstanceOwner) { + // 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; } - // Disconnect any existing frame - DisconnectFrame(); + 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 { + // mInstantiating is true if we're in LoadObject; we shouldn't + // recreate frames in that case, we'd confuse that function. + if (mInstantiating) { + return NS_OK; + } - // Set up relationship between instance owner and frame. - nsObjectFrame *objFrame = static_cast(aFrame); - mInstanceOwner->SetFrame(objFrame); - objFrame->SetInstanceOwner(mInstanceOwner); + // Trigger frame construction + mInstantiating = PR_TRUE; - // Set up new frame to draw. - objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size()); - objFrame->Invalidate(objFrame->GetContentRectRelativeToSelf()); + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); - return NS_OK; + nsIDocument* doc = thisContent->GetCurrentDoc(); + if (!doc) { + // Nothing we can do while plugin loading is done in layout... + mInstantiating = PR_FALSE; + return NS_OK; + } + + doc->FlushPendingNotifications(Flush_Frames); + + mInstantiating = PR_FALSE; + + frame = GetExistingFrame(eFlushContent); + if (!frame) { + return NS_OK; + } + } + + nsIFrame *nsiframe = do_QueryFrame(frame); + + if (nsiframe->GetStateBits() & NS_FRAME_FIRST_REFLOW) { + // A frame for this plugin element already exists now, but it has + // not been reflowed yet. Force a reflow now so that we don't end + // up initializing a plugin before knowing its size. Also re-fetch + // the frame, as flushing can cause the frame to be deleted. + frame = GetExistingFrame(eFlushLayout); + + if (!frame) { + return NS_OK; + } + + nsiframe = do_QueryFrame(frame); + } + + nsWeakFrame weakFrame(nsiframe); + + // We may have a plugin instance already; if so, do nothing + nsresult rv = frame->GetPluginInstance(aInstance); + if (!*aInstance && weakFrame.IsAlive()) { + rv = Instantiate(frame, mContentType, mURI); + if (NS_SUCCEEDED(rv) && weakFrame.IsAlive()) { + rv = frame->GetPluginInstance(aInstance); + } else { + Fallback(PR_TRUE); + } + } + return rv; } NS_IMETHODIMP -nsObjectLoadingContent::DisconnectFrame() +nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) { - if (mInstanceOwner) { - mInstanceOwner->SetFrame(nsnull); + LOG(("OBJLC [%p]: Got frame %p (mInstantiating=%i)\n", this, aFrame, + mInstantiating)); + + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); + nsIDocument* doc = thisContent->GetOwnerDoc(); + if (!doc || doc->IsStaticDocument() || doc->IsBeingUsedAsImage()) { + return NS_OK; } + // "revoke" any existing instantiate event as it likely has out of + // date data (frame pointer etc). + mPendingInstantiateEvent = nsnull; + + nsRefPtr instance; + aFrame->GetPluginInstance(getter_AddRefs(instance)); + + if (instance) { + // The frame already has a plugin instance, that means the plugin + // has already been instantiated. + + return NS_OK; + } + + if (!mInstantiating && mType == eType_Plugin) { + // Asynchronously call Instantiate + // This can go away once plugin loading moves to content + // This must be done asynchronously to ensure that the frame is correctly + // initialized (has a view etc) + + // When in a plugin document, the document will take care of calling + // instantiate + nsCOMPtr pDoc (do_QueryInterface(GetOurDocument())); + if (pDoc) { + PRBool willHandleInstantiation; + pDoc->GetWillHandleInstantiation(&willHandleInstantiation); + if (willHandleInstantiation) { + return NS_OK; + } + } + + nsIFrame* frame = do_QueryFrame(aFrame); + nsCOMPtr event = + new nsAsyncInstantiateEvent(this, frame, mContentType, mURI); + if (!event) { + return NS_ERROR_OUT_OF_MEMORY; + } + + LOG((" dispatching event\n")); + nsresult rv = NS_DispatchToCurrentThread(event); + if (NS_FAILED(rv)) { + NS_ERROR("failed to dispatch nsAsyncInstantiateEvent"); + } else { + // Remember this event. This is a weak reference that will be cleared + // when the event runs. + mPendingInstantiateEvent = event; + } + } return NS_OK; } @@ -1062,11 +998,12 @@ nsObjectLoadingContent::GetPluginInstance(nsNPAPIPluginInstance** aInstance) { *aInstance = nsnull; - if (!mInstanceOwner) { + nsIObjectFrame* objFrame = GetExistingFrame(eDontFlush); + if (!objFrame) { return NS_OK; } - return mInstanceOwner->GetInstance(aInstance); + return objFrame->GetPluginInstance(aInstance); } NS_IMETHODIMP @@ -1229,6 +1166,9 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, const nsCString& aTypeHint, PRBool aForceLoad) { + LOG(("OBJLC [%p]: Loading object: URI=<%p> notify=%i type=<%s> forceload=%i\n", + this, aURI, aNotify, aTypeHint.get(), aForceLoad)); + if (mURI && aURI && !aForceLoad) { PRBool equal; nsresult rv = mURI->Equals(aURI, &equal); @@ -1240,6 +1180,7 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, // Need to revoke any potentially pending instantiate events if (mType == eType_Plugin && mPendingInstantiateEvent) { + LOG(("OBJLC [%p]: Revoking pending instantiate event\n", this)); mPendingInstantiateEvent = nsnull; } @@ -1378,7 +1319,7 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, } break; case eType_Plugin: - rv = StartPluginInstance(); + rv = TryInstantiate(mContentType, mURI); break; case eType_Document: if (aURI) { @@ -1413,6 +1354,7 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, if (hasID && !isSupportedClassID) { // We have a class ID and it's unsupported. Fallback in that case. + LOG(("OBJLC [%p]: invalid classid\n", this)); rv = NS_ERROR_NOT_AVAILABLE; return NS_OK; } @@ -1422,6 +1364,7 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, GetTypeOfContent(aTypeHint) == eType_Plugin)) { // No URI, but we have a type. The plugin will handle the load. // Or: supported class id, plugin will handle the load. + LOG(("OBJLC [%p]: (classid) Changing type from %u to eType_Plugin\n", this, mType)); mType = eType_Plugin; // At this point, the stored content type @@ -1445,14 +1388,13 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, } } - // rv is references by a stack-based object, need to assign here - rv = StartPluginInstance(); - - return rv; + rv = TryInstantiate(mContentType, mURI); + return NS_OK; } if (!aURI) { // No URI and if we have got this far no enabled plugin supports the type + LOG(("OBJLC [%p]: no URI\n", this)); rv = NS_ERROR_NOT_AVAILABLE; // We should only notify the UI if there is at least a type to go on for @@ -1466,6 +1408,7 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, // E.g. mms:// if (!CanHandleURI(aURI)) { + LOG(("OBJLC [%p]: can't handle URI\n", this)); if (aTypeHint.IsEmpty()) { rv = NS_ERROR_NOT_AVAILABLE; return NS_OK; @@ -1473,6 +1416,8 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, if (IsSupportedPlugin(aTypeHint)) { mType = eType_Plugin; + + rv = TryInstantiate(aTypeHint, aURI); } else { rv = NS_ERROR_NOT_AVAILABLE; // No plugin to load, notify of the failure. @@ -1558,6 +1503,8 @@ nsObjectLoadingContent::GetCapabilities() const void nsObjectLoadingContent::Fallback(PRBool aNotify) { + LOG(("OBJLC [%p]: Falling back (Notify=%i)\n", this, aNotify)); + AutoNotifier notifier(this, aNotify); UnloadContent(); @@ -1566,6 +1513,7 @@ nsObjectLoadingContent::Fallback(PRBool aNotify) void nsObjectLoadingContent::RemovedFromDocument() { + LOG(("OBJLC [%p]: Removed from doc\n", this)); if (mFrameLoader) { // XXX This is very temporary and must go away mFrameLoader->Destroy(); @@ -1575,19 +1523,6 @@ nsObjectLoadingContent::RemovedFromDocument() // have already loaded the content. mURI = nsnull; } - - // When a plugin instance node is removed from the document we'll - // let the plugin continue to run at least until we get back to - // the event loop. If we get back to the event loop and the node - // has still not been added back to the document then we stop - // the plugin. - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - nsCOMPtr event = new InDocCheckEvent(thisContent); - - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - appShell->RunInStableState(event); - } } /* static */ @@ -1852,10 +1787,12 @@ nsObjectLoadingContent::GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI) } } -nsObjectFrame* +nsIObjectFrame* nsObjectLoadingContent::GetExistingFrame(FlushType aFlushType) { - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); nsIFrame* frame; do { @@ -1880,7 +1817,7 @@ nsObjectLoadingContent::GetExistingFrame(FlushType aFlushType) } while (1); nsIObjectFrame* objFrame = do_QueryFrame(frame); - return static_cast(objFrame); + return objFrame; } void @@ -1900,6 +1837,105 @@ nsObjectLoadingContent::HandleBeingBlockedByContentPolicy(nsresult aStatus, } } +nsresult +nsObjectLoadingContent::TryInstantiate(const nsACString& aMIMEType, + nsIURI* aURI) +{ + nsIObjectFrame* frame = GetExistingFrame(eFlushContent); + if (!frame) { + LOG(("OBJLC [%p]: No frame yet\n", this)); + return NS_OK; // Not a failure to have no frame + } + + nsRefPtr instance; + frame->GetPluginInstance(getter_AddRefs(instance)); + + if (!instance) { + // The frame has no plugin instance yet. If the frame hasn't been + // reflowed yet, do nothing as once the reflow happens we'll end up + // instantiating the plugin with the correct size n' all (which + // isn't known until we've done the first reflow). But if the + // frame does have a plugin instance already, be sure to + // re-instantiate the plugin as its source or whatnot might have + // chanced since it was instantiated. + nsIFrame* iframe = do_QueryFrame(frame); + if (iframe->GetStateBits() & NS_FRAME_FIRST_REFLOW) { + LOG(("OBJLC [%p]: Frame hasn't been reflowed yet\n", this)); + return NS_OK; // Not a failure to have no frame + } + } + + return Instantiate(frame, aMIMEType, aURI); +} + +nsresult +nsObjectLoadingContent::Instantiate(nsIObjectFrame* aFrame, + const nsACString& aMIMEType, + nsIURI* aURI) +{ + NS_ASSERTION(aFrame, "Must have a frame here"); + + // We're instantiating now, invalidate any pending async instantiate + // calls. + mPendingInstantiateEvent = nsnull; + + // Mark that we're instantiating now so that we don't end up + // re-entering instantiation code. + PRBool oldInstantiatingValue = mInstantiating; + mInstantiating = PR_TRUE; + + nsCString typeToUse(aMIMEType); + if (typeToUse.IsEmpty() && aURI) { + IsPluginEnabledByExtension(aURI, typeToUse); + } + + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); + + nsCOMPtr baseURI; + if (!aURI) { + // We need some URI. If we have nothing else, use the base URI. + // XXX(biesi): The code used to do this. Not sure why this is correct... + GetObjectBaseURI(thisContent, getter_AddRefs(baseURI)); + aURI = baseURI; + } + + nsIFrame *nsiframe = do_QueryFrame(aFrame); + nsWeakFrame weakFrame(nsiframe); + + // We'll always have a type or a URI by the time we get here + NS_ASSERTION(aURI || !typeToUse.IsEmpty(), "Need a URI or a type"); + LOG(("OBJLC [%p]: Calling [%p]->Instantiate(<%s>, %p)\n", this, aFrame, + typeToUse.get(), aURI)); + nsresult rv = aFrame->Instantiate(typeToUse.get(), aURI); + + mInstantiating = oldInstantiatingValue; + + nsRefPtr pluginInstance; + if (weakFrame.IsAlive()) { + aFrame->GetPluginInstance(getter_AddRefs(pluginInstance)); + } + if (pluginInstance) { + nsCOMPtr pluginTag; + nsCOMPtr host(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); + static_cast(host.get())-> + GetPluginTagForInstance(pluginInstance, getter_AddRefs(pluginTag)); + + nsCOMPtr blocklist = + do_GetService("@mozilla.org/extensions/blocklist;1"); + if (blocklist) { + PRUint32 blockState = nsIBlocklistService::STATE_NOT_BLOCKED; + blocklist->GetPluginBlocklistState(pluginTag, EmptyString(), + EmptyString(), &blockState); + if (blockState == nsIBlocklistService::STATE_OUTDATED) + FirePluginError(thisContent, ePluginOutdated); + } + } + + return rv; +} + /* static */ PluginSupportState nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent, const nsCString& aContentType) @@ -1964,7 +2000,10 @@ nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const if (thisObj->mPrintFrame.IsAlive()) { aDest->mPrintFrame = thisObj->mPrintFrame; } else { - aDest->mPrintFrame = const_cast(this)->GetExistingFrame(eDontFlush); + nsIObjectFrame* frame = + const_cast(this)->GetExistingFrame(eDontFlush); + nsIFrame* f = do_QueryFrame(frame); + aDest->mPrintFrame = f; } if (mFrameLoader) { @@ -2015,183 +2054,3 @@ nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag, } return NS_OK; } - -NS_IMETHODIMP -nsObjectLoadingContent::StartPluginInstance() -{ - // OK to have an instance already. - if (mInstanceOwner) { - return NS_OK; - } - - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - nsIDocument* doc = thisContent->GetOwnerDoc(); - if (!doc || doc->IsStaticDocument() || doc->IsBeingUsedAsImage()) { - return NS_OK; - } - - // We always start plugins on a runnable. - // We don't want a script blocker on the stack during instantiation. - nsCOMPtr event = new nsAsyncInstantiateEvent(this, mContentType, mURI); - if (!event) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsresult rv = NS_DispatchToCurrentThread(event); - if (NS_SUCCEEDED(rv)) { - // Remember this event. This is a weak reference that will be cleared - // when the event runs. - mPendingInstantiateEvent = event; - } - - return rv; -} - -static PRBool -DoDelayedStop(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop) -{ -#if (MOZ_PLATFORM_MAEMO==5) - // Don't delay stop on Maemo/Hildon (bug 530739). - if (aDelayedStop && aInstanceOwner->MatchPluginName("Shockwave Flash")) - return PR_FALSE; -#endif - - // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524), - // XStandard (bug 430219), CMISS Zinc (bug 429604). - if (aDelayedStop -#if !(defined XP_WIN || defined MOZ_X11) - && !aInstanceOwner->MatchPluginName("QuickTime") - && !aInstanceOwner->MatchPluginName("Flip4Mac") - && !aInstanceOwner->MatchPluginName("XStandard plugin") - && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin") -#endif - ) { - nsCOMPtr evt = new nsStopPluginRunnable(aInstanceOwner); - NS_DispatchToCurrentThread(evt); - return PR_TRUE; - } - return PR_FALSE; -} - -void -nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop) -{ - nsRefPtr inst; - aInstanceOwner->GetInstance(getter_AddRefs(inst)); - if (inst) { - NPWindow *win; - aInstanceOwner->GetWindow(win); - - nsPluginNativeWindow *window = (nsPluginNativeWindow *)win; - if (window) { - nsRefPtr nullinst; - window->CallSetWindow(nullinst); - } else { - inst->SetWindow(nsnull); - } - - if (DoDelayedStop(aInstanceOwner, aDelayedStop)) { - return; - } - -#if defined(XP_MACOSX) - aInstanceOwner->HidePluginWindow(); -#endif - - nsCOMPtr pluginHost = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID); - NS_ASSERTION(pluginHost, "Without a pluginHost, how can we have an instance to destroy?"); - static_cast(pluginHost.get())->StopPluginInstance(inst); - - if (window) { - window->SetPluginWidget(nsnull); - } - } - - aInstanceOwner->Destroy(); -} - -NS_IMETHODIMP -nsObjectLoadingContent::StopPluginInstance() -{ - if (!mInstanceOwner) { - return NS_OK; - } - - DisconnectFrame(); - - PRBool delayedStop = PR_FALSE; -#ifdef XP_WIN - // Force delayed stop for Real plugin only; see bug 420886, 426852. - nsRefPtr inst; - mInstanceOwner->GetInstance(getter_AddRefs(inst)); - if (inst) { - const char* mime = nsnull; - if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) { - if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) { - delayedStop = PR_TRUE; - } - } - } -#endif - - DoStopPlugin(mInstanceOwner, delayedStop); - - mInstanceOwner = nsnull; - - return NS_OK; -} - -void -nsObjectLoadingContent::TryNotifyContentObjectWrapper() -{ - if (!mInstanceOwner) { - return; - } - - nsRefPtr inst; - mInstanceOwner->GetInstance(getter_AddRefs(inst)); - if (!inst) { - return; - } - - NotifyContentObjectWrapper(); -} - -void -nsObjectLoadingContent::NotifyContentObjectWrapper() -{ - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - - nsCOMPtr doc = thisContent->GetDocument(); - if (!doc) - return; - - nsIScriptGlobalObject *sgo = doc->GetScriptGlobalObject(); - if (!sgo) - return; - - nsIScriptContext *scx = sgo->GetContext(); - if (!scx) - return; - - JSContext *cx = (JSContext *)scx->GetNativeContext(); - - nsCOMPtr wrapper; - nsContentUtils::XPConnect()-> - GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), thisContent, - NS_GET_IID(nsISupports), - getter_AddRefs(wrapper)); - - if (!wrapper) { - // 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; - - nsHTMLPluginObjElementSH::SetupProtoChain(wrapper, cx, obj); -} diff --git a/content/base/src/nsObjectLoadingContent.h b/content/base/src/nsObjectLoadingContent.h index a96894240ddf..17f29884c610 100644 --- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -53,15 +53,11 @@ #include "nsIObjectLoadingContent.h" #include "nsIRunnable.h" #include "nsIFrame.h" -#include "nsPluginInstanceOwner.h" -#include "nsIThreadInternal.h" class nsAsyncInstantiateEvent; -class nsStopPluginRunnable; class AutoNotifier; class AutoFallback; class AutoSetInstantiatingToFalse; -class nsObjectFrame; enum PluginSupportState { ePluginUnsupported, // The plugin is not supported (e.g. not installed) @@ -100,8 +96,6 @@ class nsObjectLoadingContent : public nsImageLoadingContent friend class AutoNotifier; friend class AutoFallback; friend class AutoSetInstantiatingToFalse; - friend class nsStopPluginRunnable; - friend class nsAsyncInstantiateEvent; public: // This enum's values must be the same as the constants on @@ -144,16 +138,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent { mNetworkCreated = aNetworkCreated; } - - // Both "InstantiatePluginInstance" methods can flush layout. - nsresult InstantiatePluginInstance(nsIChannel* aChannel, - nsIStreamListener** aStreamListener); - nsresult InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI); - - void NotifyOwnerDocumentActivityChanged(); - protected: - /** * Load the object from the given URI. * @param aURI The URI to load. @@ -241,14 +226,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent nsCycleCollectionTraversalCallback &cb); void CreateStaticClone(nsObjectLoadingContent* aDest) const; - - static void DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop); - private: - - void TryNotifyContentObjectWrapper(); - void NotifyContentObjectWrapper(); - /** * Check whether the given request represents a successful load. */ @@ -327,7 +305,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent eFlushLayout, eDontFlush }; - nsObjectFrame* GetExistingFrame(FlushType aFlushType); + nsIObjectFrame* GetExistingFrame(FlushType aFlushType); /** * Handle being blocked by a content policy. aStatus is the nsresult @@ -337,6 +315,22 @@ class nsObjectLoadingContent : public nsImageLoadingContent void HandleBeingBlockedByContentPolicy(nsresult aStatus, PRInt16 aRetval); + /** + * Checks if we have a frame that's ready for instantiation, and + * if so, calls Instantiate(). Note that this can cause the frame + * to be deleted while we're instantiating the plugin. + */ + nsresult TryInstantiate(const nsACString& aMIMEType, nsIURI* aURI); + + /** + * Instantiates the plugin. This differs from + * GetFrame()->Instantiate() in that it ensures that the URI will + * be non-null, and that a MIME type will be passed. Note that + * this can cause the frame to be deleted while we're + * instantiating the plugin. + */ + nsresult Instantiate(nsIObjectFrame* aFrame, const nsACString& aMIMEType, nsIURI* aURI); + /** * Get the plugin support state for the given content node and MIME type. * This is used for purposes of determining whether to fire PluginNotFound @@ -420,7 +414,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent nsWeakFrame mPrintFrame; - nsRefPtr mInstanceOwner; + friend class nsAsyncInstantiateEvent; }; + #endif diff --git a/content/html/document/src/PluginDocument.cpp b/content/html/document/src/PluginDocument.cpp index 6237d1b44265..1e26d39f4c84 100644 --- a/content/html/document/src/PluginDocument.cpp +++ b/content/html/document/src/PluginDocument.cpp @@ -47,7 +47,6 @@ #include "nsContentPolicyUtils.h" #include "nsIPropertyBag2.h" #include "mozilla/dom/Element.h" -#include "nsObjectLoadingContent.h" namespace mozilla { namespace dom { @@ -145,13 +144,20 @@ PluginStreamListener::SetupPlugin() // nsObjectFrame does that at the end of reflow. shell->FlushPendingNotifications(Flush_Layout); - nsCOMPtr olc(do_QueryInterface(embed)); - if (!olc) { + nsIFrame* frame = embed->GetPrimaryFrame(); + if (!frame) { + mPluginDoc->AllowNormalInstantiation(); + return NS_OK; + } + + nsIObjectFrame* objFrame = do_QueryFrame(frame); + if (!objFrame) { + mPluginDoc->AllowNormalInstantiation(); return NS_ERROR_UNEXPECTED; } - nsObjectLoadingContent* olcc = static_cast(olc.get()); - nsresult rv = olcc->InstantiatePluginInstance(mPluginDoc->GetType().get(), - mDocument->nsIDocument::GetDocumentURI()); + + nsresult rv = objFrame->Instantiate(mPluginDoc->GetType().get(), + mDocument->nsIDocument::GetDocumentURI()); if (NS_FAILED(rv)) { return rv; } @@ -349,7 +355,7 @@ PluginDocument::Print() nsIObjectFrame* objectFrame = do_QueryFrame(mPluginContent->GetPrimaryFrame()); if (objectFrame) { - nsRefPtr pi; + nsCOMPtr pi; objectFrame->GetPluginInstance(getter_AddRefs(pi)); if (pi) { NPPrint npprint; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index b150f9add74c..6b381971b423 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -9234,7 +9234,14 @@ nsHTMLPluginObjElementSH::GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *wra nsCOMPtr objlc(do_QueryInterface(content)); NS_ASSERTION(objlc, "Object nodes must implement nsIObjectLoadingContent"); - return objlc->GetPluginInstance(_result); + // 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. @@ -9338,9 +9345,20 @@ nsHTMLPluginObjElementSH::SetupProtoChain(nsIXPConnectWrappedNative *wrapper, if (!pi_obj) { // 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 + // 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. diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index be192e22e8a0..14d244338c62 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -80,6 +80,7 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin) #endif mRunning(NOT_STARTED), mWindowless(PR_FALSE), + mWindowlessLocal(PR_FALSE), mTransparent(PR_FALSE), mUsesDOMForCursor(PR_FALSE), mInPluginInitCall(PR_FALSE), @@ -659,6 +660,12 @@ NPError nsNPAPIPluginInstance::SetWindowless(PRBool aWindowless) return NPERR_NO_ERROR; } +NPError nsNPAPIPluginInstance::SetWindowlessLocal(PRBool aWindowlessLocal) +{ + mWindowlessLocal = aWindowlessLocal; + return NPERR_NO_ERROR; +} + NPError nsNPAPIPluginInstance::SetTransparent(PRBool aTransparent) { mTransparent = aTransparent; diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index bec98dedef10..67061dcf6161 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -124,6 +124,8 @@ public: NPError SetWindowless(PRBool aWindowless); + NPError SetWindowlessLocal(PRBool aWindowlessLocal); + NPError SetTransparent(PRBool aTransparent); NPError SetWantsAllNetworkStreams(PRBool aWantsAllNetworkStreams); @@ -212,6 +214,7 @@ protected: // these are used to store the windowless properties // which the browser will later query PRPackedBool mWindowless; + PRPackedBool mWindowlessLocal; PRPackedBool mTransparent; PRPackedBool mUsesDOMForCursor; diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index d324d6e678a4..7c2c5d13f454 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -3656,6 +3656,12 @@ nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow) return PLUG_NewPluginNativeWindow(aPluginNativeWindow); } +nsresult +nsPluginHost::DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow) +{ + return PLUG_DeletePluginNativeWindow(aPluginNativeWindow); +} + nsresult nsPluginHost::InstantiateDummyJavaPlugin(nsIPluginInstanceOwner *aOwner) { diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h index e291c75d2384..4a537bac8f59 100644 --- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -148,6 +148,7 @@ public: char **outPostData, PRUint32 *outPostDataLen); nsresult CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile); nsresult NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow); + nsresult DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow); nsresult InstantiateDummyJavaPlugin(nsIPluginInstanceOwner *aOwner); void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, PRBool isVisible); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index ee17da7c26c3..6d529df1f751 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -99,7 +99,6 @@ using mozilla::DefaultXDisplay; static NS_DEFINE_CID(kRangeCID, NS_RANGE_CID); #include "nsWidgetsCID.h" -static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #ifdef XP_WIN @@ -178,7 +177,7 @@ nsPluginInstanceOwner::NotifyPaintWaiter(nsDisplayListBuilder* aBuilder) #ifdef XP_MACOSX static void DrawPlugin(ImageContainer* aContainer, void* aPluginInstanceOwner) { - nsObjectFrame* frame = static_cast(aPluginInstanceOwner)->GetFrame(); + nsObjectFrame* frame = static_cast(aPluginInstanceOwner)->GetOwner(); if (frame) { frame->UpdateImageLayer(aContainer, gfxRect(0,0,0,0)); } @@ -285,16 +284,14 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() // create nsPluginNativeWindow object, it is derived from NPWindow // struct and allows to manipulate native window procedure nsCOMPtr pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID); - mPluginHost = static_cast(pluginHostCOM.get()); - if (mPluginHost) - mPluginHost->NewPluginNativeWindow(&mPluginWindow); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); + if (pluginHost) + pluginHost->NewPluginNativeWindow(&mPluginWindow); else mPluginWindow = nsnull; mObjectFrame = nsnull; - mContent = nsnull; mTagText = nsnull; - mWidgetCreationComplete = PR_FALSE; #ifdef XP_MACOSX memset(&mCGPluginPortCopy, 0, sizeof(NP_CGContext)); #ifndef NP_NO_QUICKDRAW @@ -313,6 +310,7 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mNumCachedParams = 0; mCachedAttrParamNames = nsnull; mCachedAttrParamValues = nsnull; + mDestroyWidget = PR_FALSE; #ifdef XP_MACOSX #ifndef NP_NO_QUICKDRAW @@ -369,8 +367,13 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mTagText = nsnull; } - PLUG_DeletePluginNativeWindow(mPluginWindow); - mPluginWindow = nsnull; + // clean up plugin native window object + nsCOMPtr pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); + if (pluginHost) { + pluginHost->DeletePluginNativeWindow(mPluginWindow); + mPluginWindow = nsnull; + } if (mInstance) { mInstance->InvalidateOwner(); @@ -1105,6 +1108,7 @@ nsresult nsPluginInstanceOwner::EnsureCachedAttrParamArrays() !mCachedAttrParamNames, "re-cache of attrs/params not implemented! use the DOM " "node directy instead"); + NS_ENSURE_TRUE(mObjectFrame, NS_ERROR_NULL_POINTER); // Convert to a 16-bit count. Subtract 2 in case we add an extra // "src" or "wmode" entry below. @@ -1177,6 +1181,9 @@ nsresult nsPluginInstanceOwner::EnsureCachedAttrParamArrays() } } + // We're done with DOM method calls now. Make sure we still have a frame. + NS_ENSURE_TRUE(mObjectFrame, NS_ERROR_OUT_OF_MEMORY); + // Convert to a 16-bit count. PRUint32 cparams = ourParams.Count(); if (cparams < 0x0000FFFF) { @@ -2448,15 +2455,70 @@ nsPluginInstanceOwner::Destroy() if (mWidget) { nsCOMPtr pluginWidget = do_QueryInterface(mWidget); - if (pluginWidget) { + if (pluginWidget) pluginWidget->SetPluginInstanceOwner(nsnull); - } - mWidget->Destroy(); + + if (mDestroyWidget) + mWidget->Destroy(); } return NS_OK; } +/* + * Prepare to stop + */ +void +nsPluginInstanceOwner::PrepareToStop(PRBool aDelayedStop) +{ + // Drop image reference because the child may destroy the surface after we return. + nsRefPtr container = mObjectFrame->GetImageContainer(); + if (container) { +#ifdef XP_MACOSX + nsRefPtr image = container->GetCurrentImage(); + if (image && (image->GetFormat() == Image::MAC_IO_SURFACE) && mObjectFrame) { + // Undo what we did to the current image in SetCurrentImage(). + MacIOSurfaceImage *oglImage = static_cast(image.get()); + oglImage->SetUpdateCallback(nsnull, nsnull); + oglImage->SetDestroyCallback(nsnull); + // If we have a current image here, its destructor hasn't yet been + // called, so OnDestroyImage() can't yet have been called. So we need + // to do ourselves what OnDestroyImage() would have done. + NS_RELEASE_THIS(); + } +#endif + container->SetCurrentImage(nsnull); + } + +#if defined(XP_WIN) || defined(MOZ_X11) + if (aDelayedStop && mWidget) { + // To delay stopping a plugin we need to reparent the plugin + // so that we can safely tear down the + // plugin after its frame (and view) is gone. + + // Also hide and disable the widget to avoid it from appearing in + // odd places after reparenting it, but before it gets destroyed. + mWidget->Show(PR_FALSE); + mWidget->Enable(PR_FALSE); + + // Reparent the plugins native window. This relies on the widget + // and plugin et al not holding any other references to its + // parent. + mWidget->SetParent(nsnull); + + mDestroyWidget = PR_TRUE; + } +#endif + + // Unregister scroll position listeners + for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { + nsIScrollableFrame* sf = do_QueryFrame(f); + if (sf) { + sf->RemoveScrollPositionListener(this); + } + } +} + // Paints are handled differently, so we just simulate an update event. #ifdef XP_MACOSX @@ -2792,19 +2854,25 @@ void nsPluginInstanceOwner::CancelTimer() } #endif -nsresult nsPluginInstanceOwner::Init(nsObjectFrame* aFrame, nsIContent* aContent) +nsresult nsPluginInstanceOwner::Init(nsPresContext* aPresContext, + nsObjectFrame* aFrame, + nsIContent* aContent) { mLastEventloopNestingLevel = GetEventloopNestingLevel(); + mObjectFrame = aFrame; mContent = aContent; - if (aFrame) { - SetFrame(aFrame); - // Some plugins require a specific sequence of shutdown and startup when - // a page is reloaded. Shutdown happens usually when the last instance - // is destroyed. Here we make sure the plugin instance in the old - // document is destroyed before we try to create the new one. - aFrame->PresContext()->EnsureVisible(); + nsWeakFrame weakFrame(aFrame); + + // Some plugins require a specific sequence of shutdown and startup when + // a page is reloaded. Shutdown happens usually when the last instance + // is destroyed. Here we make sure the plugin instance in the old + // document is destroyed before we try to create the new one. + aPresContext->EnsureVisible(); + + if (!weakFrame.IsAlive()) { + return NS_ERROR_NOT_AVAILABLE; } // register context menu listener @@ -2844,6 +2912,16 @@ nsresult nsPluginInstanceOwner::Init(nsObjectFrame* aFrame, nsIContent* aContent mContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, PR_TRUE); mContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, PR_TRUE); mContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, PR_TRUE); + + // Register scroll position listeners + // We need to register a scroll position listener on every scrollable + // frame up to the top + for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { + nsIScrollableFrame* sf = do_QueryFrame(f); + if (sf) { + sf->AddScrollPositionListener(this); + } + } return NS_OK; } @@ -2885,99 +2963,88 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) { NS_ENSURE_TRUE(mPluginWindow, NS_ERROR_NULL_POINTER); - nsresult rv = NS_ERROR_FAILURE; - - // Can't call this twice! - if (mWidget) { - NS_WARNING("Trying to create a plugin widget twice!"); - return NS_ERROR_FAILURE; - } - - PRBool windowless = PR_FALSE; - mInstance->IsWindowless(&windowless); - if (!windowless && !nsIWidget::UsePuppetWidgets()) { - // Try to get a parent widget, on some platforms widget creation will fail without - // a parent. - nsCOMPtr parentWidget; - if (mContent) { - nsIDocument *doc = mContent->GetOwnerDoc(); - if (doc) { - parentWidget = nsContentUtils::WidgetForDocument(doc); - } - } - - mWidget = do_CreateInstance(kWidgetCID, &rv); - if (NS_FAILED(rv)) { - return rv; - } - - nsWidgetInitData initData; - initData.mWindowType = eWindowType_plugin; - initData.mUnicode = PR_FALSE; - initData.clipChildren = PR_TRUE; - initData.clipSiblings = PR_TRUE; - rv = mWidget->Create(parentWidget.get(), nsnull, nsIntRect(0,0,0,0), - nsnull, nsnull, nsnull, nsnull, &initData); - if (NS_FAILED(rv)) { - mWidget->Destroy(); - mWidget = nsnull; - return rv; - } - - mWidget->EnableDragDrop(PR_TRUE); - mWidget->Show(PR_FALSE); - mWidget->Enable(PR_FALSE); - } + nsresult rv = NS_ERROR_FAILURE; if (mObjectFrame) { - // This has to be called even if we don't have a widget! The object - // frame will do windowless setup. - mObjectFrame->SetWidget(mWidget); - } + if (!mWidget) { + PRBool windowless = PR_FALSE; + mInstance->IsWindowless(&windowless); - if (windowless) { - mPluginWindow->type = NPWindowTypeDrawable; - - // this needs to be a HDC according to the spec, but I do - // not see the right way to release it so let's postpone - // passing HDC till paint event when it is really - // needed. Change spec? - mPluginWindow->window = nsnull; + // always create widgets in Twips, not pixels + nsPresContext* context = mObjectFrame->PresContext(); + rv = mObjectFrame->CreateWidget(context->DevPixelsToAppUnits(mPluginWindow->width), + context->DevPixelsToAppUnits(mPluginWindow->height), + windowless); + if (NS_OK == rv) { + mWidget = mObjectFrame->GetWidget(); + + if (PR_TRUE == windowless) { + mPluginWindow->type = NPWindowTypeDrawable; + + // this needs to be a HDC according to the spec, but I do + // not see the right way to release it so let's postpone + // passing HDC till paint event when it is really + // needed. Change spec? + mPluginWindow->window = nsnull; #ifdef MOZ_X11 - // Fill in the display field. - NPSetWindowCallbackStruct* ws_info = - static_cast(mPluginWindow->ws_info); - ws_info->display = DefaultXDisplay(); - - nsCAutoString description; - GetPluginDescription(description); - NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10."); - mFlash10Quirks = StringBeginsWith(description, flash10Head); + // Fill in the display field. + NPSetWindowCallbackStruct* ws_info = + static_cast(mPluginWindow->ws_info); + ws_info->display = DefaultXDisplay(); + + nsCAutoString description; + GetPluginDescription(description); + NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10."); + mFlash10Quirks = StringBeginsWith(description, flash10Head); #endif - } else if (mWidget) { - // mPluginWindow->type is used in |GetPluginPort| so it must - // be initialized first - mPluginWindow->type = NPWindowTypeWindow; - mPluginWindow->window = GetPluginPortFromWidget(); - + + // Changing to windowless mode changes the NPWindow geometry. + mObjectFrame->FixupWindow(mObjectFrame->GetContentRectRelativeToSelf().Size()); + } else if (mWidget) { + nsIWidget* parent = mWidget->GetParent(); + NS_ASSERTION(parent, "Plugin windows must not be toplevel"); + // Set the plugin window to have an empty cliprect. The cliprect + // will be reset when nsRootPresContext::UpdatePluginGeometry + // runs later. The plugin window does need to have the correct + // size here. GetEmptyClipConfiguration will probably give it the + // size, but just in case we haven't been reflowed or something, set + // the size explicitly. + nsAutoTArray configuration; + mObjectFrame->GetEmptyClipConfiguration(&configuration); + if (configuration.Length() > 0) { + configuration[0].mBounds.width = mPluginWindow->width; + configuration[0].mBounds.height = mPluginWindow->height; + } + parent->ConfigureChildren(configuration); + + // mPluginWindow->type is used in |GetPluginPort| so it must + // be initialized first + mPluginWindow->type = NPWindowTypeWindow; + mPluginWindow->window = GetPluginPortFromWidget(); + #ifdef MAC_CARBON_PLUGINS - // start the idle timer. - StartTimer(PR_TRUE); + // start the idle timer. + StartTimer(PR_TRUE); #endif - - // tell the plugin window about the widget - mPluginWindow->SetPluginWidget(mWidget); - - // tell the widget about the current plugin instance owner. - nsCOMPtr pluginWidget = do_QueryInterface(mWidget); - if (pluginWidget) { - pluginWidget->SetPluginInstanceOwner(this); + + // tell the plugin window about the widget + mPluginWindow->SetPluginWidget(mWidget); + + // tell the widget about the current plugin instance owner. + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (pluginWidget) + pluginWidget->SetPluginInstanceOwner(this); + } + } } } - mWidgetCreationComplete = PR_TRUE; + return rv; +} - return NS_OK; +void nsPluginInstanceOwner::SetPluginHost(nsIPluginHost* aHost) +{ + mPluginHost = static_cast(aHost); } // Mac specific code to fix up the port location and clipping region @@ -3221,85 +3288,6 @@ nsPluginInstanceOwner::CallSetWindow() } } -void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame) -{ - // Don't do anything if the frame situation hasn't changed. - if (mObjectFrame == aFrame) { - return; - } - - // Deal with things that depend on whether or not we used to have a frame. - if (mObjectFrame) { - // We have an old frame. - // Drop image reference because the child may destroy the surface after we return. - nsRefPtr container = mObjectFrame->GetImageContainer(); - if (container) { -#ifdef XP_MACOSX - nsRefPtr image = container->GetCurrentImage(); - if (image && (image->GetFormat() == Image::MAC_IO_SURFACE) && mObjectFrame) { - // Undo what we did to the current image in SetCurrentImage(). - MacIOSurfaceImage *oglImage = static_cast(image.get()); - oglImage->SetUpdateCallback(nsnull, nsnull); - oglImage->SetDestroyCallback(nsnull); - // If we have a current image here, its destructor hasn't yet been - // called, so OnDestroyImage() can't yet have been called. So we need - // to do ourselves what OnDestroyImage() would have done. - NS_RELEASE_THIS(); - } -#endif - container->SetCurrentImage(nsnull); - } - - // If we had an old frame and we're not going to have a new one then - // we should unregister for some things. - if (!aFrame) { - // Unregister scroll position listeners - for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { - nsIScrollableFrame* sf = do_QueryFrame(f); - if (sf) { - sf->RemoveScrollPositionListener(this); - } - } - } - - // Make sure the old frame isn't holding a reference to us. - mObjectFrame->SetInstanceOwner(nsnull); - } else { - if (aFrame) { - // We didn't have an object frame before but we do now! - // We need to register a scroll position listener on every scrollable - // frame up to the top - for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { - nsIScrollableFrame* sf = do_QueryFrame(f); - if (sf) { - sf->AddScrollPositionListener(this); - } - } - } - } - - // Swap in the new frame (or no frame) - mObjectFrame = aFrame; - - // Set up a new frame - if (mObjectFrame) { - mObjectFrame->SetInstanceOwner(this); - // Can only call SetWidget on an object frame once. Don't do it here unless - // widget creation is complete. Whether or not one was actually created and - // mWidget is NULL is irrelevant. - if (mWidgetCreationComplete) { - mObjectFrame->SetWidget(mWidget); - } - mObjectFrame->FixupWindow(mObjectFrame->GetContentRectRelativeToSelf().Size()); - mObjectFrame->Invalidate(mObjectFrame->GetContentRectRelativeToSelf()); - } -} - -nsObjectFrame* nsPluginInstanceOwner::GetFrame() -{ - return mObjectFrame; -} - // Little helper function to resolve relative URL in // |value| for certain inputs of |name| void nsPluginInstanceOwner::FixUpURLS(const nsString &name, nsAString &value) diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index d2936bd44322..2b11351655d4 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -132,7 +132,9 @@ public: nsresult KeyPress(nsIDOMEvent* aKeyEvent); nsresult Destroy(); - + + void PrepareToStop(PRBool aDelayedStop); + #ifdef XP_WIN void Paint(const RECT& aDirty, HDC aDC); #elif defined(XP_MACOSX) @@ -159,11 +161,14 @@ public: //locals - nsresult Init(nsObjectFrame* aFrame, nsIContent* aContent); + nsresult Init(nsPresContext* aPresContext, nsObjectFrame* aFrame, + nsIContent* aContent); void* GetPluginPortFromWidget(); void ReleasePluginPort(void* pluginPort); - + + void SetPluginHost(nsIPluginHost* aHost); + nsEventStatus ProcessEvent(const nsGUIEvent & anEvent); #ifdef XP_MACOSX @@ -202,10 +207,15 @@ public: void UpdateDocumentActiveState(PRBool aIsActive); #endif // XP_MACOSX void CallSetWindow(); - - void SetFrame(nsObjectFrame *aFrame); - nsObjectFrame* GetFrame(); - + + void SetOwner(nsObjectFrame *aOwner) + { + mObjectFrame = aOwner; + } + nsObjectFrame* GetOwner() { + return mObjectFrame; + } + PRUint32 GetLastEventloopNestingLevel() const { return mLastEventloopNestingLevel; } @@ -298,11 +308,10 @@ private: nsPluginNativeWindow *mPluginWindow; nsRefPtr mInstance; - nsObjectFrame *mObjectFrame; - nsIContent *mContent; // WEAK, content owns us + nsObjectFrame *mObjectFrame; // owns nsPluginInstanceOwner + nsCOMPtr mContent; nsCString mDocumentBase; char *mTagText; - PRBool mWidgetCreationComplete; nsCOMPtr mWidget; nsRefPtr mPluginHost; @@ -338,7 +347,10 @@ private: #endif PRPackedBool mPluginWindowVisible; PRPackedBool mPluginDocumentActiveState; - + + // If true, destroy the widget on destruction. Used when plugin stop + // is being delayed to a safer point in time. + PRPackedBool mDestroyWidget; PRUint16 mNumCachedAttrs; PRUint16 mNumCachedParams; char **mCachedAttrParamNames; diff --git a/dom/plugins/test/mochitest/Makefile.in b/dom/plugins/test/mochitest/Makefile.in index a7e3a8a42ad2..0b12274ff2c7 100644 --- a/dom/plugins/test/mochitest/Makefile.in +++ b/dom/plugins/test/mochitest/Makefile.in @@ -102,11 +102,6 @@ _MOCHITEST_FILES = \ test_zero_opacity.html \ test_NPPVpluginWantsAllNetworkStreams.html \ test_npruntime_npnsetexception.html \ - test_display_none.html \ - test_instance_re-parent.html \ - test_instance_unparent1.html \ - test_instance_unparent2.html \ - test_instance_unparent3.html \ $(NULL) # test_plugin_scroll_painting.html \ bug 596491 @@ -131,7 +126,6 @@ _MOCHICHROME_FILES = \ ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa) _MOCHITEST_FILES += \ - test_instance_re-parent-windowed.html \ test_visibility.html \ $(NULL) diff --git a/dom/plugins/test/mochitest/test_display_none.html b/dom/plugins/test/mochitest/test_display_none.html deleted file mode 100644 index 907aa187c2e2..000000000000 --- a/dom/plugins/test/mochitest/test_display_none.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - Test npruntime and paint count for instance in a display:none div - - - - - -

- -
- -
- - - - diff --git a/dom/plugins/test/mochitest/test_instance_re-parent-windowed.html b/dom/plugins/test/mochitest/test_instance_re-parent-windowed.html deleted file mode 100644 index cc128db387e2..000000000000 --- a/dom/plugins/test/mochitest/test_instance_re-parent-windowed.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - Test re-parentinging an instance's DOM node - - - - - -

- -
- -
-
-
- - - - diff --git a/dom/plugins/test/mochitest/test_instance_re-parent.html b/dom/plugins/test/mochitest/test_instance_re-parent.html deleted file mode 100644 index e075d715731e..000000000000 --- a/dom/plugins/test/mochitest/test_instance_re-parent.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - Test re-parentinging an instance's DOM node - - - - - -

- -
- -
-
-
- - - - diff --git a/dom/plugins/test/mochitest/test_instance_unparent1.html b/dom/plugins/test/mochitest/test_instance_unparent1.html deleted file mode 100644 index a9dea72489a2..000000000000 --- a/dom/plugins/test/mochitest/test_instance_unparent1.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - Test removing an instance's DOM node - - - - - -

- -
- -
- - - - diff --git a/dom/plugins/test/mochitest/test_instance_unparent2.html b/dom/plugins/test/mochitest/test_instance_unparent2.html deleted file mode 100644 index 7756c0e2db32..000000000000 --- a/dom/plugins/test/mochitest/test_instance_unparent2.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - Test removing an instance's DOM node - - - - - -

- -
- -
- -
-
- -
-
- - - - diff --git a/dom/plugins/test/mochitest/test_instance_unparent3.html b/dom/plugins/test/mochitest/test_instance_unparent3.html deleted file mode 100644 index 92f8e89765e7..000000000000 --- a/dom/plugins/test/mochitest/test_instance_unparent3.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - Test removing an instance's DOM node - - - - - -

- -
-
- - - - - - diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 3e0359936a1a..0f96d2719b55 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -7581,9 +7581,10 @@ PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet) static void FreezeElement(nsIContent *aContent, void * /* unused */) { - nsCOMPtr olc(do_QueryInterface(aContent)); - if (olc) { - olc->StopPluginInstance(); + nsIFrame *frame = aContent->GetPrimaryFrame(); + nsIObjectFrame *objectFrame = do_QueryFrame(frame); + if (objectFrame) { + objectFrame->StopPlugin(); } } @@ -7661,9 +7662,10 @@ PresShell::FireOrClearDelayedEvents(PRBool aFireEvents) static void ThawElement(nsIContent *aContent, void *aShell) { - nsCOMPtr olc(do_QueryInterface(aContent)); - if (olc) { - olc->StartPluginInstance(); + nsCOMPtr objlc(do_QueryInterface(aContent)); + if (objlc) { + nsRefPtr inst; + objlc->EnsureInstantiation(getter_AddRefs(inst)); } } diff --git a/layout/generic/nsIObjectFrame.h b/layout/generic/nsIObjectFrame.h index cddb93a5ce19..dfb04648094a 100644 --- a/layout/generic/nsIObjectFrame.h +++ b/layout/generic/nsIObjectFrame.h @@ -54,6 +54,42 @@ public: NS_IMETHOD GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) = 0; + /** + * Instantiate a plugin for a channel, returning a stream listener for the + * data. + * + * @note Calling this method can delete the frame, so don't assume + * the frame is alive after this call returns. + */ + virtual nsresult Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamListener) = 0; + + /** + * @note Calling this method can delete the frame, so don't assume + * the frame is alive after this call returns. + */ + virtual void TryNotifyContentObjectWrapper() = 0; + + /** + * Instantiate a plugin that loads the data itself. + * @param aMimeType Type of the plugin to instantiate. May be null. + * @param aURI URI of the plugin data. May be null. + * @note Only one of aURI and aMimeType may be null. + * If aURI is null, aMimeType must not be the empty string. + * @note XXX this method is here only temporarily, until plugins are loaded + * from content. + * + * @note Calling this method can delete the frame, so don't assume + * the frame is alive after this call returns. + */ + virtual nsresult Instantiate(const char* aMimeType, nsIURI* aURI) = 0; + + /** + * Stops and unloads the plugin. Makes the frame ready to receive another + * Instantiate() call. It is safe to call this method even when no plugin + * is currently active in this frame. + */ + virtual void StopPlugin() = 0; + /** * Get the native widget for the plugin, if any. */ diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 070b24eee351..ad1503ae984d 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -322,11 +322,17 @@ NS_IMETHODIMP nsObjectFrame::GetPluginPort(HWND *aPort) #endif #endif +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); + NS_IMETHODIMP nsObjectFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow) { + NS_PRECONDITION(aContent, "How did that happen?"); + mPreventInstantiation = + (aContent->GetCurrentDoc()->GetDisplayDocument() != nsnull); + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("Initializing nsObjectFrame %p for content %p\n", this, aContent)); @@ -338,17 +344,25 @@ nsObjectFrame::Init(nsIContent* aContent, void nsObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) { - // Tell content owner of the instance to disconnect its frame. - nsCOMPtr objContent(do_QueryInterface(mContent)); - NS_ASSERTION(objContent, "Why not an object loading content?"); - objContent->DisconnectFrame(); + NS_ASSERTION(!mPreventInstantiation || + (mContent && mContent->GetCurrentDoc()->GetDisplayDocument()), + "about to crash due to bug 136927"); + + // we need to finish with the plugin before native window is destroyed + // doing this in the destructor is too late. + StopPluginInternal(PR_TRUE); + + // StopPluginInternal might have disowned the widget; if it has, + // mWidget will be null. + if (mWidget) { + mInnerView->DetachWidgetEventHandler(mWidget); + mWidget->Destroy(); + } if (mBackgroundSink) { mBackgroundSink->Destroy(); } - SetInstanceOwner(nsnull); - nsObjectFrameSuper::DestroyFrom(aDestructRoot); } @@ -383,14 +397,20 @@ nsObjectFrame::GetFrameName(nsAString& aResult) const #endif nsresult -nsObjectFrame::SetWidget(nsIWidget *aWidget) +nsObjectFrame::CreateWidget(nscoord aWidth, + nscoord aHeight, + PRBool aViewOnly) { - mWidget = aWidget; - nsIView* view = GetView(); NS_ASSERTION(view, "Object frames must have views"); if (!view) { - return NS_ERROR_FAILURE; + return NS_OK; //XXX why OK? MMP + } + + PRBool needsWidget = !aViewOnly; + PRBool canCreateWidget = !nsIWidget::UsePuppetWidgets(); + if (needsWidget && !canCreateWidget) { + NS_WARNING("Can't use native widgets, and can't hand a plugins a PuppetWidget"); } nsIViewManager* viewMan = view->GetViewManager(); @@ -398,6 +418,9 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) // XXX is the above comment correct? viewMan->SetViewVisibility(view, nsViewVisibility_kHide); + nsRefPtr dx; + viewMan->GetDeviceContext(*getter_AddRefs(dx)); + //this is ugly. it was ripped off from didreflow(). MMP // Position and size view relative to its parent, not relative to our // parent frame (our parent frame may not have a view). @@ -415,12 +438,12 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) return NS_ERROR_FAILURE; } - if (mWidget) { + if (needsWidget && !mWidget && canCreateWidget) { // XXX this breaks plugins in popups ... do we care? - nsIWidget* parentWidget = rpc->PresShell()->FrameManager()->GetRootFrame()->GetNearestWidget(); - if (!parentWidget) { + nsIWidget* parentWidget = + rpc->PresShell()->FrameManager()->GetRootFrame()->GetNearestWidget(); + if (!parentWidget) return NS_ERROR_FAILURE; - } mInnerView = viewMan->CreateView(GetContentRectRelativeToSelf(), view); if (!mInnerView) { @@ -429,27 +452,30 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) } viewMan->InsertChild(view, mInnerView, nsnull, PR_TRUE); - mWidget->SetParent(parentWidget); - mWidget->Show(PR_TRUE); - mWidget->Enable(PR_TRUE); + nsresult rv; + mWidget = do_CreateInstance(kWidgetCID, &rv); + if (NS_FAILED(rv)) + return rv; - // Set the plugin window to have an empty cliprect. The cliprect - // will be reset when nsRootPresContext::UpdatePluginGeometry - // runs later. The plugin window does need to have the correct - // size here. GetEmptyClipConfiguration will probably give it the - // size, but just in case we haven't been reflowed or something, set - // the size explicitly. - nsAutoTArray configuration; - GetEmptyClipConfiguration(&configuration); - NS_ASSERTION(configuration.Length() > 0, "Empty widget configuration array!"); - configuration[0].mBounds.width = mRect.width; - configuration[0].mBounds.height = mRect.height; - parentWidget->ConfigureChildren(configuration); - - nsRefPtr dx; - viewMan->GetDeviceContext(*getter_AddRefs(dx)); + nsWidgetInitData initData; + initData.mWindowType = eWindowType_plugin; + initData.mUnicode = PR_FALSE; + initData.clipChildren = PR_TRUE; + initData.clipSiblings = PR_TRUE; + // We want mWidget to be able to deliver events to us, especially on + // Mac where events to the plugin are routed through Gecko. So we + // allow the view to attach its event handler to mWidget even though + // mWidget isn't the view's designated widget. EVENT_CALLBACK eventHandler = mInnerView->AttachWidgetEventHandler(mWidget); - mWidget->SetEventCallback(eventHandler, dx); + rv = mWidget->Create(parentWidget, nsnull, nsIntRect(0,0,0,0), + eventHandler, dx, nsnull, nsnull, &initData); + if (NS_FAILED(rv)) { + mWidget->Destroy(); + mWidget = nsnull; + return rv; + } + + mWidget->EnableDragDrop(PR_TRUE); // If this frame has an ancestor with a widget which is not // the root prescontext's widget, then this plugin should not be @@ -465,17 +491,19 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) Invalidate(GetContentRectRelativeToSelf()); #endif } - + } + + if (mWidget) { rpc->RegisterPluginForGeometryUpdates(this); rpc->RequestUpdatePluginGeometry(this); - + // Here we set the background color for this widget because some plugins will use // the child window background color when painting. If it's not set, it may default to gray // Sometimes, a frame doesn't have a background color or is transparent. In this // case, walk up the frame tree until we do find a frame with a background color for (nsIFrame* frame = this; frame; frame = frame->GetParent()) { nscolor bgcolor = - frame->GetVisitedDependentColor(eCSSProperty_background_color); + frame->GetVisitedDependentColor(eCSSProperty_background_color); if (NS_GET_A(bgcolor) > 0) { // make sure we got an actual color mWidget->SetBackgroundColor(bgcolor); break; @@ -486,9 +514,8 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) // Now that we have a widget we want to set the event model before // any events are processed. nsCOMPtr pluginWidget = do_QueryInterface(mWidget); - if (!pluginWidget) { + if (!pluginWidget) return NS_ERROR_FAILURE; - } pluginWidget->SetPluginEventModel(mInstanceOwner->GetEventModel()); pluginWidget->SetPluginDrawingModel(mInstanceOwner->GetDrawingModel()); @@ -497,9 +524,6 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) } #endif } else { - // Changing to windowless mode changes the NPWindow geometry. - FixupWindow(GetContentRectRelativeToSelf().Size()); - #ifndef XP_MACOSX rpc->RegisterPluginForGeometryUpdates(this); rpc->RequestUpdatePluginGeometry(this); @@ -510,14 +534,7 @@ nsObjectFrame::SetWidget(nsIWidget *aWidget) viewMan->SetViewVisibility(view, nsViewVisibility_kShow); } -#ifdef ACCESSIBILITY - nsAccessibilityService* accService = nsIPresShell::AccService(); - if (accService) { - accService->RecreateAccessible(PresContext()->PresShell(), mContent); - } -#endif - - return NS_OK; + return (needsWidget && !canCreateWidget) ? NS_ERROR_NOT_AVAILABLE : NS_OK; } #define EMBED_DEF_WIDTH 240 @@ -676,6 +693,50 @@ nsObjectFrame::ReflowCallbackCanceled() mReflowCallbackPosted = PR_FALSE; } +nsresult +nsObjectFrame::InstantiatePlugin(nsPluginHost* aPluginHost, + const char* aMimeType, + nsIURI* aURI) +{ + NS_ASSERTION(mPreventInstantiation, + "Instantiation should be prevented here!"); + + // If you add early return(s), be sure to balance this call to + // appShell->SuspendNative() with additional call(s) to + // appShell->ReturnNative(). + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) { + appShell->SuspendNative(); + } + + NS_ASSERTION(mContent, "We should have a content node."); + + nsIDocument* doc = mContent->GetOwnerDoc(); + nsCOMPtr pDoc (do_QueryInterface(doc)); + PRBool fullPageMode = PR_FALSE; + if (pDoc) { + pDoc->GetWillHandleInstantiation(&fullPageMode); + } + + nsresult rv; + if (fullPageMode) { /* full-page mode */ + nsCOMPtr stream; + rv = aPluginHost->InstantiateFullPagePlugin(aMimeType, aURI, mInstanceOwner, getter_AddRefs(stream)); + if (NS_SUCCEEDED(rv)) + pDoc->SetStreamListener(stream); + } else { /* embedded mode */ + rv = aPluginHost->InstantiateEmbeddedPlugin(aMimeType, aURI, mInstanceOwner); + } + + // Note that |this| may very well be destroyed already! + + if (appShell) { + appShell->ResumeNative(); + } + + return rv; +} + void nsObjectFrame::FixupWindow(const nsSize& aSize) { @@ -771,38 +832,6 @@ nsObjectFrame::CallSetWindow(PRBool aCheckIsHidden) return rv; } -void -nsObjectFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner) -{ - mInstanceOwner = aOwner; - if (!mInstanceOwner) { - nsRootPresContext* rpc = PresContext()->GetRootPresContext(); - if (rpc) { - if (mWidget) { - if (mInnerView) { - mInnerView->DetachWidgetEventHandler(mWidget); - } - - rpc->UnregisterPluginForGeometryUpdates(this); - // Make sure the plugin is hidden in case an update of plugin geometry - // hasn't happened since this plugin became hidden. - nsIWidget* parent = mWidget->GetParent(); - if (parent) { - nsTArray configurations; - this->GetEmptyClipConfiguration(&configurations); - parent->ConfigureChildren(configurations); - - mWidget->SetParent(nsnull); - } - } else { -#ifndef XP_MACOSX - rpc->UnregisterPluginForGeometryUpdates(this); -#endif - } - } - } -} - PRBool nsObjectFrame::IsFocusable(PRInt32 *aTabIndex, PRBool aWithMouse) { @@ -2048,11 +2077,400 @@ nsObjectFrame::GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) { *aPluginInstance = nsnull; - if (!mInstanceOwner) { + if (!mInstanceOwner) + return NS_OK; + + return mInstanceOwner->GetInstance(aPluginInstance); +} + +nsresult +nsObjectFrame::PrepareInstanceOwner() +{ + nsWeakFrame weakFrame(this); + + // First, have to stop any possibly running plugins. + StopPluginInternal(PR_FALSE); + + if (!weakFrame.IsAlive()) { + return NS_ERROR_NOT_AVAILABLE; + } + + NS_ASSERTION(!mInstanceOwner, "Must not have an instance owner here"); + + mInstanceOwner = new nsPluginInstanceOwner(); + + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("Created new instance owner %p for frame %p\n", mInstanceOwner.get(), + this)); + + // Note, |this| may very well be gone after this call. + return mInstanceOwner->Init(PresContext(), this, GetContent()); +} + +nsresult +nsObjectFrame::Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamListener) +{ + if (mPreventInstantiation) { + return NS_OK; + } + + // Note: If PrepareInstanceOwner() returns an error, |this| may very + // well be deleted already. + nsresult rv = PrepareInstanceOwner(); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv)); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); + if (NS_FAILED(rv)) { + return rv; + } + + mInstanceOwner->SetPluginHost(pluginHostCOM); + + // This must be done before instantiating the plugin + FixupWindow(GetContentRectRelativeToSelf().Size()); + + // Ensure we redraw when a plugin is instantiated + Invalidate(GetContentRectRelativeToSelf()); + + nsWeakFrame weakFrame(this); + + NS_ASSERTION(!mPreventInstantiation, "Say what?"); + mPreventInstantiation = PR_TRUE; + rv = pluginHost->InstantiatePluginForChannel(aChannel, mInstanceOwner, aStreamListener); + + if (!weakFrame.IsAlive()) { + return NS_ERROR_NOT_AVAILABLE; + } + + NS_ASSERTION(mPreventInstantiation, + "Instantiation should still be prevented!"); + mPreventInstantiation = PR_FALSE; + +#ifdef ACCESSIBILITY + nsAccessibilityService* accService = nsIPresShell::AccService(); + if (accService) { + accService->RecreateAccessible(PresContext()->PresShell(), mContent); + } +#endif + + return rv; +} + +nsresult +nsObjectFrame::Instantiate(const char* aMimeType, nsIURI* aURI) +{ + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsObjectFrame::Instantiate(%s) called on frame %p\n", aMimeType, + this)); + + if (mPreventInstantiation) { return NS_OK; } - return mInstanceOwner->GetInstance(aPluginInstance); + // XXXbz can aMimeType ever actually be null here? If not, either + // the callers are wrong (and passing "" instead of null) or we can + // remove the codepaths dealing with null aMimeType in + // InstantiateEmbeddedPlugin. + NS_ASSERTION(aMimeType || aURI, "Need a type or a URI!"); + + // Note: If PrepareInstanceOwner() returns an error, |this| may very + // well be deleted already. + nsresult rv = PrepareInstanceOwner(); + NS_ENSURE_SUCCESS(rv, rv); + + nsWeakFrame weakFrame(this); + + // This must be done before instantiating the plugin + FixupWindow(GetContentRectRelativeToSelf().Size()); + + // Ensure we redraw when a plugin is instantiated + Invalidate(GetContentRectRelativeToSelf()); + + // get the nsIPluginHost service + nsCOMPtr pluginHost(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return rv; + mInstanceOwner->SetPluginHost(pluginHost); + + NS_ASSERTION(!mPreventInstantiation, "Say what?"); + mPreventInstantiation = PR_TRUE; + + rv = InstantiatePlugin(static_cast(pluginHost.get()), aMimeType, aURI); + + if (!weakFrame.IsAlive()) { + return NS_ERROR_NOT_AVAILABLE; + } + + // finish up + if (NS_SUCCEEDED(rv)) { + TryNotifyContentObjectWrapper(); + + if (!weakFrame.IsAlive()) { + return NS_ERROR_NOT_AVAILABLE; + } + + CallSetWindow(); + } + + NS_ASSERTION(mPreventInstantiation, + "Instantiation should still be prevented!"); + +#ifdef ACCESSIBILITY + nsAccessibilityService* accService = nsIPresShell::AccService(); + if (accService) { + accService->RecreateAccessible(PresContext()->PresShell(), mContent); + } +#endif + + mPreventInstantiation = PR_FALSE; + + return rv; +} + +void +nsObjectFrame::TryNotifyContentObjectWrapper() +{ + nsRefPtr inst; + mInstanceOwner->GetInstance(getter_AddRefs(inst)); + if (inst) { + // The plugin may have set up new interfaces; we need to mess with our JS + // wrapper. Note that we DO NOT want to call this if there is no plugin + // instance! That would just reenter Instantiate(), trying to create + // said plugin instance. + NotifyContentObjectWrapper(); + } +} + +class nsStopPluginRunnable : public nsRunnable, public nsITimerCallback +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + nsStopPluginRunnable(nsPluginInstanceOwner *aInstanceOwner) + : mInstanceOwner(aInstanceOwner) + { + NS_ASSERTION(aInstanceOwner, "need an owner"); + } + + // nsRunnable + NS_IMETHOD Run(); + + // nsITimerCallback + NS_IMETHOD Notify(nsITimer *timer); + +private: + nsCOMPtr mTimer; + nsRefPtr mInstanceOwner; +}; + +NS_IMPL_ISUPPORTS_INHERITED1(nsStopPluginRunnable, nsRunnable, nsITimerCallback) + +#if defined(XP_WIN) +static const char* +GetMIMEType(nsNPAPIPluginInstance *aPluginInstance) +{ + if (aPluginInstance) { + const char* mime = nsnull; + if (NS_SUCCEEDED(aPluginInstance->GetMIMEType(&mime)) && mime) + return mime; + } + return ""; +} +#endif // XP_WIN + +static PRBool +DoDelayedStop(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop) +{ +#if (MOZ_PLATFORM_MAEMO==5) + // Don't delay stop on Maemo/Hildon (bug 530739). + if (aDelayedStop && aInstanceOwner->MatchPluginName("Shockwave Flash")) + return PR_FALSE; +#endif + + // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524), + // XStandard (bug 430219), CMISS Zinc (bug 429604). + if (aDelayedStop +#if !(defined XP_WIN || defined MOZ_X11) + && !aInstanceOwner->MatchPluginName("QuickTime") + && !aInstanceOwner->MatchPluginName("Flip4Mac") + && !aInstanceOwner->MatchPluginName("XStandard plugin") + && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin") +#endif + ) { + nsCOMPtr evt = new nsStopPluginRunnable(aInstanceOwner); + NS_DispatchToCurrentThread(evt); + return PR_TRUE; + } + return PR_FALSE; +} + +static void +DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop) +{ + nsRefPtr inst; + aInstanceOwner->GetInstance(getter_AddRefs(inst)); + if (inst) { + NPWindow *win; + aInstanceOwner->GetWindow(win); + nsPluginNativeWindow *window = (nsPluginNativeWindow *)win; + nsRefPtr nullinst; + + if (window) + window->CallSetWindow(nullinst); + else + inst->SetWindow(nsnull); + + if (DoDelayedStop(aInstanceOwner, aDelayedStop)) + return; + +#if defined(XP_MACOSX) + aInstanceOwner->HidePluginWindow(); +#endif + + nsCOMPtr pluginHost = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID); + NS_ASSERTION(pluginHost, "Without a pluginHost, how can we have an instance to destroy?"); + static_cast(pluginHost.get())->StopPluginInstance(inst); + + // the frame is going away along with its widget so tell the + // window to forget its widget too + if (window) + window->SetPluginWidget(nsnull); + } + + aInstanceOwner->Destroy(); +} + +NS_IMETHODIMP +nsStopPluginRunnable::Notify(nsITimer *aTimer) +{ + return Run(); +} + +NS_IMETHODIMP +nsStopPluginRunnable::Run() +{ + // InitWithCallback calls Release before AddRef so we need to hold a + // strong ref on 'this' since we fall through to this scope if it fails. + nsCOMPtr kungFuDeathGrip = this; + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) { + PRUint32 currentLevel = 0; + appShell->GetEventloopNestingLevel(¤tLevel); + if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) { + if (!mTimer) + mTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (mTimer) { + // Fire 100ms timer to try to tear down this plugin as quickly as + // possible once the nesting level comes back down. + nsresult rv = mTimer->InitWithCallback(this, 100, nsITimer::TYPE_ONE_SHOT); + if (NS_SUCCEEDED(rv)) { + return rv; + } + } + NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe " + "time). Stopping the plugin now, this might crash."); + } + } + + mTimer = nsnull; + + DoStopPlugin(mInstanceOwner, PR_FALSE); + + return NS_OK; +} + +void +nsObjectFrame::StopPlugin() +{ + PRBool delayedStop = PR_FALSE; +#ifdef XP_WIN + nsRefPtr inst; + if (mInstanceOwner) + mInstanceOwner->GetInstance(getter_AddRefs(inst)); + if (inst) { + // Delayed stop for Real plugin only; see bug 420886, 426852. + const char* pluginType = ::GetMIMEType(inst); + delayedStop = strcmp(pluginType, "audio/x-pn-realaudio-plugin") == 0; + } +#endif + StopPluginInternal(delayedStop); +} + +void +nsObjectFrame::StopPluginInternal(PRBool aDelayedStop) +{ + if (!mInstanceOwner) { + return; + } + + nsRootPresContext* rpc = PresContext()->GetRootPresContext(); + if (!rpc) { + NS_ASSERTION(PresContext()->PresShell()->IsFrozen(), + "unable to unregister the plugin frame"); + } + else if (mWidget) { + rpc->UnregisterPluginForGeometryUpdates(this); + + // Make sure the plugin is hidden in case an update of plugin geometry + // hasn't happened since this plugin became hidden. + nsIWidget* parent = mWidget->GetParent(); + if (parent) { + nsTArray configurations; + GetEmptyClipConfiguration(&configurations); + parent->ConfigureChildren(configurations); + } + } + else { +#ifndef XP_MACOSX + rpc->UnregisterPluginForGeometryUpdates(this); +#endif + } + + // Transfer the reference to the instance owner onto the stack so + // that if we do end up re-entering this code, or if we unwind back + // here witha deleted frame (this), we can still continue to stop + // the plugin. Note that due to that, the ordering of the code in + // this function is extremely important. + + nsRefPtr owner; + owner.swap(mInstanceOwner); + + // Make sure that our windowless rect has been zeroed out, so if we + // get reinstantiated we'll send the right messages to the plug-in. + mWindowlessRect.SetEmpty(); + + PRBool oldVal = mPreventInstantiation; + mPreventInstantiation = PR_TRUE; + + nsWeakFrame weakFrame(this); + +#if defined(XP_WIN) || defined(MOZ_X11) + if (aDelayedStop && mWidget) { + // If we're asked to do a delayed stop it means we're stopping the + // plugin because we're destroying the frame. In that case, disown + // the widget. + mInnerView->DetachWidgetEventHandler(mWidget); + mWidget = nsnull; + } +#endif + + // From this point on, |this| could have been deleted, so don't + // touch it! + owner->PrepareToStop(aDelayedStop); + + DoStopPlugin(owner, aDelayedStop); + + // If |this| is still alive, reset mPreventInstantiation. + if (weakFrame.IsAlive()) { + NS_ASSERTION(mPreventInstantiation, + "Instantiation should still be prevented!"); + + mPreventInstantiation = oldVal; + } + + // Break relationship between frame and plugin instance owner + owner->SetOwner(nsnull); } NS_IMETHODIMP @@ -2086,6 +2504,43 @@ nsObjectFrame::SetIsDocumentActive(PRBool aIsActive) #endif } +void +nsObjectFrame::NotifyContentObjectWrapper() +{ + nsCOMPtr doc = mContent->GetDocument(); + if (!doc) + return; + + nsIScriptGlobalObject *sgo = doc->GetScriptGlobalObject(); + if (!sgo) + return; + + nsIScriptContext *scx = sgo->GetContext(); + if (!scx) + return; + + JSContext *cx = (JSContext *)scx->GetNativeContext(); + + nsCOMPtr 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. 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; + + nsHTMLPluginObjElementSH::SetupProtoChain(wrapper, cx, obj); +} + // static nsIObjectFrame * nsObjectFrame::GetNextObjectFrame(nsPresContext* aPresContext, nsIFrame* aRoot) diff --git a/layout/generic/nsObjectFrame.h b/layout/generic/nsObjectFrame.h index 7b6554031dda..c141125bd584 100644 --- a/layout/generic/nsObjectFrame.h +++ b/layout/generic/nsObjectFrame.h @@ -40,6 +40,10 @@ #ifndef nsObjectFrame_h___ #define nsObjectFrame_h___ +#ifdef XP_WIN +#include +#endif + #include "nsPluginInstanceOwner.h" #include "nsIObjectFrame.h" #include "nsFrame.h" @@ -120,9 +124,20 @@ public: virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); NS_METHOD GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance); - + virtual nsresult Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamListener); + virtual nsresult Instantiate(const char* aMimeType, nsIURI* aURI); + virtual void TryNotifyContentObjectWrapper(); + virtual void StopPlugin(); virtual void SetIsDocumentActive(PRBool aIsActive); + /* + * Stop a plugin instance. If aDelayedStop is true, the plugin will + * be stopped at a later point when it's safe to do so (i.e. not + * while destroying the frame tree). Delayed stopping is only + * implemented on Win32 for now. + */ + void StopPluginInternal(PRBool aDelayedStop); + NS_IMETHOD GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor); // Compute the desired position of the plugin's widget, on the assumption @@ -148,7 +163,7 @@ public: #endif //local methods - nsresult SetWidget(nsIWidget *aWidget); + nsresult CreateWidget(nscoord aWidth, nscoord aHeight, PRBool aViewOnly); // for a given aRoot, this walks the frame tree looking for the next outFrame static nsIObjectFrame* GetNextObjectFrame(nsPresContext* aPresContext, @@ -194,19 +209,6 @@ public: nsIWidget* GetWidget() { return mWidget; } - /** - * Adjust the plugin's idea of its size, using aSize as its new size. - * (aSize must be in twips) - */ - void FixupWindow(const nsSize& aSize); - - /* - * Sets up the plugin window and calls SetWindow on the plugin. - */ - nsresult CallSetWindow(PRBool aCheckIsHidden = PR_TRUE); - - void SetInstanceOwner(nsPluginInstanceOwner* aOwner); - protected: nsObjectFrame(nsStyleContext* aContext); virtual ~nsObjectFrame(); @@ -217,6 +219,21 @@ protected: const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize); + nsresult InstantiatePlugin(nsPluginHost* aPluginHost, + const char* aMimetype, + nsIURI* aURL); + + /** + * Adjust the plugin's idea of its size, using aSize as its new size. + * (aSize must be in twips) + */ + void FixupWindow(const nsSize& aSize); + + /** + * Sets up the plugin window and calls SetWindow on the plugin. + */ + nsresult CallSetWindow(PRBool aCheckIsHidden = PR_TRUE); + PRBool IsFocusable(PRInt32 *aTabIndex = nsnull, PRBool aWithMouse = PR_FALSE); // check attributes and optionally CSS to see if we should display anything @@ -225,6 +242,8 @@ protected: PRBool IsOpaque() const; PRBool IsTransparentMode() const; + void NotifyContentObjectWrapper(); + nsIntPoint GetWindowOriginInPixels(PRBool aWindowless); static void PaintPrintPlugin(nsIFrame* aFrame, @@ -236,6 +255,12 @@ protected: nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, const nsRect& aPluginRect); + /** + * Makes sure that mInstanceOwner is valid and without a current plugin + * instance. Essentially, this prepares the frame to receive a new plugin. + */ + NS_HIDDEN_(nsresult) PrepareInstanceOwner(); + /** * Get the widget geometry for the plugin. aRegion is in some appunits * coordinate system whose origin is device-pixel-aligned (if possible), @@ -266,8 +291,8 @@ private: private: nsString mEventType; }; - - nsPluginInstanceOwner* mInstanceOwner; // WEAK + + nsRefPtr mInstanceOwner; nsIView* mInnerView; nsCOMPtr mWidget; nsIntRect mWindowlessRect; @@ -277,6 +302,11 @@ private: */ PluginBackgroundSink* mBackgroundSink; + // For assertions that make it easier to determine if a crash is due + // to the underlying problem described in bug 136927, and to prevent + // reentry into instantiation. + PRBool mPreventInstantiation; + PRPackedBool mReflowCallbackPosted; // A reference to the ImageContainer which contains the current frame diff --git a/layout/generic/nsPluginUtilsOSX.mm b/layout/generic/nsPluginUtilsOSX.mm index 7d4030f95de6..4949cd62ff75 100644 --- a/layout/generic/nsPluginUtilsOSX.mm +++ b/layout/generic/nsPluginUtilsOSX.mm @@ -157,12 +157,7 @@ NPBool NS_NPAPI_ConvertPointCocoa(void* inView, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace) { - // Plugins don't always have a view/frame. It would be odd to ask for a point conversion - // without a view, so we'll warn about it, but it's technically OK. - if (!inView) { - NS_WARNING("Must have a native view to convert coordinates."); - return PR_FALSE; - } + NS_ASSERTION(inView, "Must have a native view to convert coordinates."); // Caller has to want a result. if (!destX && !destY)