From 394171431212a039b707b97bba0db98fccb89b76 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 25 Jul 2012 22:39:01 +0200 Subject: [PATCH] Back out bug 771666 and bug 745030. --- .../base/public/nsIObjectLoadingContent.idl | 3 - content/base/src/nsObjectLoadingContent.cpp | 2445 ++++++++--------- content/base/src/nsObjectLoadingContent.h | 460 ++-- .../html/content/src/nsHTMLObjectElement.cpp | 47 +- .../content/src/nsHTMLSharedObjectElement.cpp | 37 +- content/html/document/src/PluginDocument.cpp | 3 +- .../base/nsPluginStreamListenerPeer.cpp | 2 +- .../tests/chrome/sandbox_content.html | 4 +- .../identity/tests/chrome/test_sandbox.xul | 28 +- 9 files changed, 1366 insertions(+), 1663 deletions(-) diff --git a/content/base/public/nsIObjectLoadingContent.idl b/content/base/public/nsIObjectLoadingContent.idl index 02df0defdc71..2e1f346a3132 100644 --- a/content/base/public/nsIObjectLoadingContent.idl +++ b/content/base/public/nsIObjectLoadingContent.idl @@ -24,9 +24,6 @@ interface nsIURI; [scriptable, uuid(e3e284a3-b4a8-49ef-af6b-c8c4a158db86)] interface nsIObjectLoadingContent : nsISupports { - /** - * See notes in nsObjectLoadingContent.h - */ const unsigned long TYPE_LOADING = 0; const unsigned long TYPE_IMAGE = 1; const unsigned long TYPE_PLUGIN = 2; diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 519019050033..b0baf9730ef9 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -80,66 +80,61 @@ static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc"); #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG) -/// -/// Runnables and helper classes -/// - class nsAsyncInstantiateEvent : public nsRunnable { public: - nsAsyncInstantiateEvent(nsObjectLoadingContent *aContent) - : mContent(aContent) {} + nsObjectLoadingContent *mContent; + nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent) + : mContent(aContent) + { + static_cast(mContent)->AddRef(); + } - ~nsAsyncInstantiateEvent() {} + ~nsAsyncInstantiateEvent() + { + static_cast(mContent)->Release(); + } NS_IMETHOD Run(); - -private: - nsCOMPtr mContent; }; NS_IMETHODIMP nsAsyncInstantiateEvent::Run() { - nsObjectLoadingContent *objLC = - static_cast(mContent.get()); - // do nothing if we've been revoked - if (objLC->mPendingInstantiateEvent != this) { + if (mContent->mPendingInstantiateEvent != this) { return NS_OK; } - objLC->mPendingInstantiateEvent = nsnull; + mContent->mPendingInstantiateEvent = nsnull; - return objLC->SyncStartPluginInstance(); + return mContent->SyncStartPluginInstance(); } // 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: - InDocCheckEvent(nsObjectLoadingContent *aContent) - : mContent(aContent) {} + nsCOMPtr mContent; - ~InDocCheckEvent() {} + InDocCheckEvent(nsIContent* aContent) + : mContent(aContent) + { + } + + ~InDocCheckEvent() + { + } NS_IMETHOD Run(); - -private: - nsCOMPtr mContent; }; NS_IMETHODIMP InDocCheckEvent::Run() { - nsObjectLoadingContent *objLC = - static_cast(mContent.get()); - - nsCOMPtr content = - do_QueryInterface(static_cast(objLC)); - - if (!content->IsInDoc()) { - nsObjectLoadingContent *objLC = - static_cast(mContent.get()); - objLC->UnloadObject(); + if (!mContent->IsInDoc()) { + nsCOMPtr olc = do_QueryInterface(mContent); + if (olc) { + olc->StopPluginInstance(); + } } return NS_OK; } @@ -149,51 +144,50 @@ InDocCheckEvent::Run() */ class nsPluginErrorEvent : public nsRunnable { public: - nsPluginErrorEvent(nsIContent* aContent, - nsObjectLoadingContent::FallbackType aFallbackType) + nsCOMPtr mContent; + PluginSupportState mState; + + nsPluginErrorEvent(nsIContent* aContent, PluginSupportState aState) : mContent(aContent), - mFallbackType(aFallbackType) {} + mState(aState) + {} ~nsPluginErrorEvent() {} NS_IMETHOD Run(); - -private: - nsCOMPtr mContent; - nsObjectLoadingContent::FallbackType mFallbackType; }; NS_IMETHODIMP nsPluginErrorEvent::Run() { + LOG(("OBJLC []: Firing plugin not found event for content %p\n", + mContent.get())); nsString type; - switch (mFallbackType) { - case nsObjectLoadingContent::eFallbackVulnerableUpdatable: - type = NS_LITERAL_STRING("PluginVulnerableUpdatable"); - break; - case nsObjectLoadingContent::eFallbackVulnerableNoUpdate: - type = NS_LITERAL_STRING("PluginVulnerableNoUpdate"); - break; - case nsObjectLoadingContent::eFallbackClickToPlay: + switch (mState) { + case ePluginClickToPlay: type = NS_LITERAL_STRING("PluginClickToPlay"); break; - case nsObjectLoadingContent::eFallbackUnsupported: + case ePluginVulnerableUpdatable: + type = NS_LITERAL_STRING("PluginVulnerableUpdatable"); + break; + case ePluginVulnerableNoUpdate: + type = NS_LITERAL_STRING("PluginVulnerableNoUpdate"); + break; + case ePluginUnsupported: type = NS_LITERAL_STRING("PluginNotFound"); break; - case nsObjectLoadingContent::eFallbackDisabled: + case ePluginDisabled: type = NS_LITERAL_STRING("PluginDisabled"); break; - case nsObjectLoadingContent::eFallbackBlocklisted: + case ePluginBlocklisted: type = NS_LITERAL_STRING("PluginBlocklisted"); break; - case nsObjectLoadingContent::eFallbackOutdated: + case ePluginOutdated: type = NS_LITERAL_STRING("PluginOutdated"); break; default: return NS_OK; } - LOG(("OBJLC [%p]: nsPluginErrorEvent firing '%s'", - mContent.get(), NS_ConvertUTF16toUTF8(type).get())); nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent, type, true, true); @@ -234,7 +228,7 @@ public: NS_IMETHODIMP nsPluginCrashedEvent::Run() { - LOG(("OBJLC [%p]: Firing plugin crashed event\n", + LOG(("OBJLC []: Firing plugin crashed event for content %p\n", mContent.get())); nsCOMPtr domDoc = @@ -256,7 +250,7 @@ nsPluginCrashedEvent::Run() event->InitEvent(NS_LITERAL_STRING("PluginCrashed"), true, true); event->SetTrusted(true); event->GetInternalNSEvent()->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH; - + nsCOMPtr variant; // add a "pluginDumpID" property to this event @@ -358,8 +352,7 @@ nsStopPluginRunnable::Run() 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); + nsresult rv = mTimer->InitWithCallback(this, 100, nsITimer::TYPE_ONE_SHOT); if (NS_SUCCEEDED(rv)) { return rv; } @@ -377,89 +370,82 @@ nsStopPluginRunnable::Run() return NS_OK; } -// You can't take the address of bitfield members, so we have two separate -// classes for these :-/ - -// Sets a object's mInstantiating bit to false when destroyed -class AutoSetInstantiatingToFalse { -public: - AutoSetInstantiatingToFalse(nsObjectLoadingContent *aContent) - : mContent(aContent) {} - ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; } -private: - nsObjectLoadingContent* mContent; -}; - -// Sets a object's mInstantiating bit to false when destroyed -class AutoSetLoadingToFalse { -public: - AutoSetLoadingToFalse(nsObjectLoadingContent *aContent) - : mContent(aContent) {} - ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; } -private: - nsObjectLoadingContent* mContent; -}; - -/// -/// Helper functions -/// - -static bool -IsSuccessfulRequest(nsIRequest* aRequest) -{ - nsresult status; - nsresult rv = aRequest->GetStatus(&status); - if (NS_FAILED(rv) || NS_FAILED(status)) { - return false; - } - - // This may still be an error page or somesuch - nsCOMPtr httpChan(do_QueryInterface(aRequest)); - if (httpChan) { - bool success; - rv = httpChan->GetRequestSucceeded(&success); - if (NS_FAILED(rv) || !success) { - return false; +class AutoNotifier { + public: + AutoNotifier(nsObjectLoadingContent* aContent, bool aNotify) : + mContent(aContent), mNotify(aNotify) { + mOldType = aContent->Type(); + mOldState = aContent->ObjectState(); + } + ~AutoNotifier() { + mContent->NotifyStateChanged(mOldType, mOldState, false, mNotify); } - } - // Otherwise, the request is successful - return true; -} + /** + * Send notifications now, ignoring the value of mNotify. The new type and + * state is saved, and the destructor will notify again if mNotify is true + * and the values changed. + */ + void Notify() { + NS_ASSERTION(mNotify, "Should not notify when notify=false"); -static bool -CanHandleURI(nsIURI* aURI) -{ - nsCAutoString scheme; - if (NS_FAILED(aURI->GetScheme(scheme))) { - return false; - } + mContent->NotifyStateChanged(mOldType, mOldState, true, true); + mOldType = mContent->Type(); + mOldState = mContent->ObjectState(); + } - nsIIOService* ios = nsContentUtils::GetIOService(); - if (!ios) - return false; + private: + nsObjectLoadingContent* mContent; + bool mNotify; + nsObjectLoadingContent::ObjectType mOldType; + nsEventStates mOldState; +}; - nsCOMPtr handler; - ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)); - if (!handler) { - return false; - } +/** + * A class that will automatically fall back if a |rv| variable has a failure + * code when this class is destroyed. It does not notify. + */ +class AutoFallback { + public: + AutoFallback(nsObjectLoadingContent* aContent, const nsresult* rv) + : mContent(aContent), mResult(rv), mPluginState(ePluginOtherState) {} + ~AutoFallback() { + if (NS_FAILED(*mResult)) { + LOG(("OBJLC [%p]: rv=%08x, falling back\n", mContent, *mResult)); + mContent->Fallback(false); + if (mPluginState != ePluginOtherState) { + mContent->mFallbackReason = mPluginState; + } + } + } - nsCOMPtr extHandler = - do_QueryInterface(handler); - // We can handle this URI if its protocol handler is not the external one - return extHandler == nsnull; -} + /** + * This should be set to something other than ePluginOtherState to indicate + * a specific failure that should be passed on. + */ + void SetPluginState(PluginSupportState aState) { + NS_ASSERTION(aState != ePluginOtherState, "Should not be setting ePluginOtherState"); + mPluginState = aState; + } + private: + nsObjectLoadingContent* mContent; + const nsresult* mResult; + PluginSupportState mPluginState; +}; -// Helper for tedious URI equality syntax when one or both arguments may be -// null and URIEquals(null, null) should be true -static bool inline -URIEquals(nsIURI *a, nsIURI *b) -{ - bool equal; - return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal); -} +/** + * A class that automatically sets mInstantiating to false when it goes + * out of scope. + */ +class AutoSetInstantiatingToFalse { + public: + AutoSetInstantiatingToFalse(nsObjectLoadingContent* objlc) : mContent(objlc) {} + ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; } + private: + nsObjectLoadingContent* mContent; +}; +// helper functions static bool IsSupportedImage(const nsCString& aMimeType) { @@ -473,6 +459,77 @@ IsSupportedImage(const nsCString& aMimeType) return NS_SUCCEEDED(rv) && supported; } +nsresult nsObjectLoadingContent::IsPluginEnabledForType(const nsCString& aMIMEType) +{ + nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); + if (!pluginHost) { + return NS_ERROR_FAILURE; + } + + nsresult rv = pluginHost->IsPluginEnabledForType(aMIMEType.get()); + + // Check to see if the plugin is disabled before deciding if it + // should be in the "click to play" state, since we only want to + // display "click to play" UI for enabled plugins. + if (NS_FAILED(rv)) { + return rv; + } + + if (!pluginHost->IsPluginClickToPlayForType(aMIMEType.get())) { + mCTPPlayable = true; + } + + if (!mCTPPlayable) { + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); + MOZ_ASSERT(thisContent); + nsIDocument* ownerDoc = thisContent->OwnerDoc(); + + nsCOMPtr window = ownerDoc->GetWindow(); + if (!window) { + return NS_ERROR_FAILURE; + } + nsCOMPtr topWindow; + rv = window->GetTop(getter_AddRefs(topWindow)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr topDocument; + rv = topWindow->GetDocument(getter_AddRefs(topDocument)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr topDoc = do_QueryInterface(topDocument); + + nsCOMPtr permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + bool allowPerm = false; + // For now we always say that the system principal uses click-to-play since + // that maintains current behavior and we have tests that expect this. + // What we really should do is disable plugins entirely in pages that use + // the system principal, i.e. in chrome pages. That way the click-to-play + // code here wouldn't matter at all. Bug 775301 is tracking this. + if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) { + PRUint32 permission; + rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(), + "plugins", + &permission); + NS_ENSURE_SUCCESS(rv, rv); + allowPerm = permission == nsIPermissionManager::ALLOW_ACTION; + } + + PRUint32 state; + rv = pluginHost->GetBlocklistStateForType(aMIMEType.get(), &state); + NS_ENSURE_SUCCESS(rv, rv); + + if (allowPerm && + state != nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE && + state != nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) { + mCTPPlayable = true; + } else { + return NS_ERROR_PLUGIN_CLICKTOPLAY; + } + } + + return NS_OK; +} + static void GetExtensionFromURI(nsIURI* uri, nsCString& ext) { @@ -494,105 +551,41 @@ GetExtensionFromURI(nsIURI* uri, nsCString& ext) * Checks whether a plugin exists and is enabled for the extension * in the given URI. The MIME type is returned in the mimeType out parameter. */ -bool -IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType) +bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType) { nsCAutoString ext; GetExtensionFromURI(uri, ext); + bool enabled = false; if (ext.IsEmpty()) { return false; } - nsRefPtr pluginHost = - already_AddRefed(nsPluginHost::GetInst()); - + nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); if (!pluginHost) { - NS_NOTREACHED("No pluginhost"); return false; } const char* typeFromExt; - nsresult rv = pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt); - if (NS_SUCCEEDED(rv)) { + if (NS_SUCCEEDED(pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt))) { mimeType = typeFromExt; - return true; + enabled = true; + + if (!pluginHost->IsPluginClickToPlayForType(mimeType.get())) { + mCTPPlayable = true; + } } - return false; -} -nsresult -IsPluginEnabledForType(const nsCString& aMIMEType) -{ - nsRefPtr pluginHost = - already_AddRefed(nsPluginHost::GetInst()); - - if (!pluginHost) { - NS_NOTREACHED("No pluginhost"); + if (!mCTPPlayable) { return false; + } else { + return enabled; } - - nsresult rv = pluginHost->IsPluginEnabledForType(aMIMEType.get()); - - // Check to see if the plugin is disabled before deciding if it - // should be in the "click to play" state, since we only want to - // display "click to play" UI for enabled plugins. - if (NS_FAILED(rv)) { - return rv; - } - - return NS_OK; -} - -/// -/// Member Functions -/// - -bool -nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType) -{ - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "must be a content"); - - nsresult rv; - nsCOMPtr info( - do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv)); - PRUint32 supported; - if (info) { - nsCOMPtr webNav; - nsIDocument* currentDoc = thisContent->GetCurrentDoc(); - if (currentDoc) { - webNav = do_GetInterface(currentDoc->GetScriptGlobalObject()); - } - rv = info->IsTypeSupported(aMimeType, webNav, &supported); - } - - if (NS_SUCCEEDED(rv)) { - if (supported == nsIWebNavigationInfo::UNSUPPORTED) { - // Try a stream converter - // NOTE: We treat any type we can convert from as a supported type. If a - // type is not actually supported, the URI loader will detect that and - // return an error, and we'll fallback. - nsCOMPtr convServ = - do_GetService("@mozilla.org/streamConverters;1"); - bool canConvert = false; - if (convServ) { - rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert); - } - return NS_SUCCEEDED(rv) && canConvert; - } - - // Don't want to support plugins as documents - return supported != nsIWebNavigationInfo::PLUGIN; - } - - return false; } nsresult -nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, - nsIContent* /*aParent*/, +nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* /*aParent*/, nsIContent* /*aBindingParent*/, bool /*aCompileEventHandlers*/) { @@ -605,70 +598,37 @@ nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, void nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/) { - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); MOZ_ASSERT(thisContent); nsIDocument* ownerDoc = thisContent->OwnerDoc(); ownerDoc->RemovePlugin(this); - - if (mType == eType_Plugin) { - // 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 tear down the - // plugin - nsCOMPtr event = new InDocCheckEvent(this); - - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - appShell->RunInStableState(event); - } - } else { - /// XXX(johns): The implementation for GenericFrame notes that ideally we - /// would keep the docshell around, but trash the frameloader - UnloadObject(); - } - } nsObjectLoadingContent::nsObjectLoadingContent() : mPendingInstantiateEvent(nsnull) , mChannel(nsnull) , mType(eType_Loading) - , mFallbackType(eFallbackAlternate) - , mChannelLoaded(false) , mInstantiating(false) + , mUserDisabled(false) + , mSuppressed(false) , mNetworkCreated(true) - , mActivated(false) , mIsStopping(false) - , mIsLoading(false) - , mSrcStreamLoading(false) {} + , mSrcStreamLoading(false) + , mFallbackReason(ePluginOtherState) + , mCTPPlayable(false) + , mActivated(false) {} nsObjectLoadingContent::~nsObjectLoadingContent() { - // Should have been unbound from the tree at this point, and InDocCheckEvent - // keeps us alive + DestroyImageLoadingContent(); if (mFrameLoader) { - NS_NOTREACHED("Should not be tearing down frame loaders at this point"); mFrameLoader->Destroy(); } - if (mInstanceOwner) { - // This is especially bad as delayed stop will try to hold on to this - // object... - NS_NOTREACHED("Should not be tearing down a plugin at this point!"); - StopPluginInstance(); - } - DestroyImageLoadingContent(); } nsresult -nsObjectLoadingContent::InstantiatePluginInstance() +nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI) { - if (mType != eType_Plugin) { - LOG(("OBJLC [%p]: Refusing to instantiate non-plugin, " - "type %u", this, mType)); - return NS_OK; - } - // Don't do anything if we already have an active instance. if (mInstanceOwner) { return NS_OK; @@ -685,28 +645,40 @@ nsObjectLoadingContent::InstantiatePluginInstance() // can destroy this DOM object. Don't allow that for the scope // of this method. nsCOMPtr kungFuDeathGrip = this; + 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; + } - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); // Flush layout so that the plugin is initialized with the latest information. nsIDocument* doc = thisContent->GetCurrentDoc(); if (!doc) { return NS_ERROR_FAILURE; } if (!doc->IsActive()) { - NS_ERROR("Shouldn't be calling " - "InstantiatePluginInstance in an inactive document"); + NS_ERROR("Shouldn't be calling InstantiatePluginInstance in an inactive document"); return NS_ERROR_FAILURE; } doc->FlushPendingNotifications(Flush_Layout); nsresult rv = NS_ERROR_FAILURE; - nsRefPtr pluginHost = - already_AddRefed(nsPluginHost::GetInst()); + nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv)); + nsPluginHost* pluginHost = static_cast(pluginHostCOM.get()); + if (NS_FAILED(rv)) { + return rv; + } - if (!pluginHost) { - NS_NOTREACHED("No pluginhost"); - return false; + if (!pluginHost->IsPluginClickToPlayForType(aMimeType)) { + mCTPPlayable = true; + } + + if (!mCTPPlayable) { + return NS_ERROR_PLUGIN_CLICKTOPLAY; } // If you add early return(s), be sure to balance this call to @@ -725,16 +697,13 @@ nsObjectLoadingContent::InstantiatePluginInstance() if (fullPageMode) { nsCOMPtr stream; - rv = pluginHost->InstantiateFullPagePluginInstance(mContentType.get(), - mURI.get(), this, - getter_AddRefs(mInstanceOwner), - getter_AddRefs(stream)); + rv = pluginHost->InstantiateFullPagePluginInstance(aMimeType, aURI, this, + getter_AddRefs(mInstanceOwner), getter_AddRefs(stream)); if (NS_SUCCEEDED(rv)) { pDoc->SetStreamListener(stream); } } else { - rv = pluginHost->InstantiateEmbeddedPluginInstance(mContentType.get(), - mURI.get(), this, + rv = pluginHost->InstantiateEmbeddedPluginInstance(aMimeType, aURI, this, getter_AddRefs(mInstanceOwner)); } @@ -753,20 +722,20 @@ nsObjectLoadingContent::InstantiatePluginInstance() GetPluginInstance(getter_AddRefs(pluginInstance)); if (pluginInstance) { nsCOMPtr pluginTag; - pluginHost->GetPluginTagForInstance(pluginInstance, - getter_AddRefs(pluginTag)); - + pluginHost->GetPluginTagForInstance(pluginInstance, getter_AddRefs(pluginTag)); + nsCOMPtr blocklist = - do_GetService("@mozilla.org/extensions/blocklist;1"); + 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(eFallbackOutdated); + FirePluginError(thisContent, ePluginOutdated); } } + mActivated = true; return NS_OK; } @@ -777,8 +746,7 @@ nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged() return; } - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); nsIDocument* ownerDoc = thisContent->OwnerDoc(); if (!ownerDoc->IsActive()) { StopPluginInstance(); @@ -790,43 +758,261 @@ NS_IMETHODIMP nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) { - /// This must call LoadObject, even upon failure, to allow it to either - /// proceed with the load, or trigger fallback content. - SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest"); - LOG(("OBJLC [%p]: Channel OnStartRequest", this)); - if (aRequest != mChannel || !aRequest) { - // happens when a new load starts before the previous one got here + // This is a bit of an edge case - happens when a new load starts before the + // previous one got here return NS_BINDING_ABORTED; } - NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?"); - NS_ASSERTION(!mFinalListener, "mFinalListener exists already?"); + AutoNotifier notifier(this, true); - mChannelLoaded = true; + if (!IsSuccessfulRequest(aRequest)) { + LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this)); + Fallback(false); + return NS_BINDING_ABORTED; + } nsCOMPtr chan(do_QueryInterface(aRequest)); NS_ASSERTION(chan, "Why is our request not a channel?"); - nsCOMPtr uri; + nsresult rv = NS_ERROR_UNEXPECTED; + // This fallback variable MUST be declared after the notifier variable. Do NOT + // change the order of the declarations! + AutoFallback fallback(this, &rv); - if (IsSuccessfulRequest(aRequest)) { - chan->GetURI(getter_AddRefs(uri)); + nsCString channelType; + rv = chan->GetContentType(channelType); + NS_ENSURE_SUCCESS(rv, rv); + + if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) { + channelType = APPLICATION_OCTET_STREAM; + chan->SetContentType(channelType); } + // We want to ignore the channel type if one of the following is true: + // + // 1) The channel type is application/octet-stream or binary/octet-stream + // and we have a type hint (in mContentType) and the type hint is not a + // document type. + // 2) Our type hint is a type that we support with a plugin + // (where "support" means it is enabled or it is click-to-play) + // and this object loading content has the capability to load a plugin. + // We have to be careful here - there might be a plugin that supports + // image types, so make sure the type of the content is not an image. + bool isOctetStream = (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) || + channelType.EqualsASCII(BINARY_OCTET_STREAM)); + ObjectType typeOfContent = GetTypeOfContent(mContentType); + bool caseOne = (isOctetStream && + !mContentType.IsEmpty() && + typeOfContent != eType_Document); + nsresult pluginState = IsPluginEnabledForType(mContentType); + bool pluginSupported = (NS_SUCCEEDED(pluginState) || + pluginState == NS_ERROR_PLUGIN_CLICKTOPLAY); + PRUint32 caps = GetCapabilities(); + bool caseTwo = (pluginSupported && + (caps & eSupportPlugins) && + typeOfContent != eType_Image && + typeOfContent != eType_Document); + if (caseOne || caseTwo) { + // 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()) { + chan->SetContentType(typeHint); + } + } else { + mContentType = channelType; + } + + nsCOMPtr uri; + chan->GetURI(getter_AddRefs(uri)); if (!uri) { - LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this)); - // If the request fails, we still call LoadObject() to handle fallback - // content and notifying of failure. (mChannelLoaded && !mChannel) indicates - // the bad state. - mChannel = nsnull; - LoadObject(true, false); return NS_ERROR_FAILURE; } - return LoadObject(true, false, aRequest); + if (mContentType.EqualsASCII(APPLICATION_OCTET_STREAM) || + mContentType.EqualsASCII(BINARY_OCTET_STREAM)) { + nsCAutoString extType; + if (IsPluginEnabledByExtension(uri, extType)) { + mContentType = extType; + chan->SetContentType(extType); + } + } + + // Now find out what type the content is + // UnloadContent will set our type to null; need to be sure to only set it to + // the real value on success + ObjectType newType = GetTypeOfContent(mContentType); + LOG(("OBJLC [%p]: OnStartRequest: Content Type=<%s> Old type=%u New Type=%u\n", + this, mContentType.get(), mType, newType)); + + // Now do a content policy check + // XXXbz this duplicates some code in nsContentBlocker::ShouldLoad + PRInt32 contentPolicyType; + switch (newType) { + case eType_Image: + contentPolicyType = nsIContentPolicy::TYPE_IMAGE; + break; + case eType_Document: + contentPolicyType = nsIContentPolicy::TYPE_SUBDOCUMENT; + break; + default: + contentPolicyType = nsIContentPolicy::TYPE_OBJECT; + break; + } + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); + + nsIDocument* doc = thisContent->OwnerDoc(); + + PRInt16 shouldProcess = nsIContentPolicy::ACCEPT; + rv = + NS_CheckContentProcessPolicy(contentPolicyType, + uri, + doc->NodePrincipal(), + static_cast(this), + mContentType, + nsnull, //extra + &shouldProcess, + nsContentUtils::GetContentPolicy(), + nsContentUtils::GetSecurityManager()); + if (NS_FAILED(rv) || NS_CP_REJECTED(shouldProcess)) { + HandleBeingBlockedByContentPolicy(rv, shouldProcess); + rv = NS_OK; // otherwise, the AutoFallback will make us fall back + return NS_BINDING_ABORTED; + } + + if (mType != newType) { + UnloadContent(); + } + + switch (newType) { + case eType_Image: + rv = LoadImageWithChannel(chan, getter_AddRefs(mFinalListener)); + NS_ENSURE_SUCCESS(rv, rv); + + // If we have a success result but no final listener, then the image is + // cached. In that case, we can just return: No need to try to call the + // final listener. + if (!mFinalListener) { + mType = newType; + return NS_BINDING_ABORTED; + } + break; + case eType_Document: { + if (!mFrameLoader) { + mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(), + mNetworkCreated); + if (!mFrameLoader) { + Fallback(false); + return NS_ERROR_UNEXPECTED; + } + } + + rv = mFrameLoader->CheckForRecursiveLoad(uri); + if (NS_FAILED(rv)) { + Fallback(false); + return rv; + } + + if (mType != newType) { + // XXX We must call this before getting the docshell to work around + // bug 300540; when that's fixed, this if statement can be removed. + mType = newType; + notifier.Notify(); + + if (!mFrameLoader) { + // mFrameLoader got nulled out when we notified, which most + // likely means the node was removed from the + // document. Abort the load that just started. + return NS_BINDING_ABORTED; + } + } + + // We're loading a document, so we have to set LOAD_DOCUMENT_URI + // (especially important for firing onload) + nsLoadFlags flags = 0; + chan->GetLoadFlags(&flags); + flags |= nsIChannel::LOAD_DOCUMENT_URI; + chan->SetLoadFlags(flags); + + nsCOMPtr docShell; + rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr req(do_QueryInterface(docShell)); + NS_ASSERTION(req, "Docshell must be an ifreq"); + + nsCOMPtr + uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + rv = uriLoader->OpenChannel(chan, nsIURILoader::DONT_RETARGET, req, + getter_AddRefs(mFinalListener)); + break; + } + case eType_Plugin: { + if (mType != newType) { + mType = newType; + notifier.Notify(); + } + nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); + if (!pluginHost) { + return NS_ERROR_NOT_AVAILABLE; + } + pluginHost->NewEmbeddedPluginStreamListener(uri, this, nsnull, + getter_AddRefs(mFinalListener)); + break; + } + case eType_Loading: + NS_NOTREACHED("Should not have a loading type here!"); + case eType_Null: + // 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. + Fallback(false); + + PluginSupportState pluginState = GetPluginSupportState(thisContent, + mContentType); + // Do nothing, but fire the plugin not found event if needed + if (pluginState != ePluginOtherState) { + mFallbackReason = pluginState; + FirePluginError(thisContent, pluginState); + } + return NS_BINDING_ABORTED; + } + + if (mFinalListener) { + mType = newType; + + mSrcStreamLoading = true; + rv = mFinalListener->OnStartRequest(aRequest, aContext); + mSrcStreamLoading = false; + + if (NS_SUCCEEDED(rv)) { + // Plugins need to set up for NPRuntime. + if (mType == eType_Plugin) { + NotifyContentObjectWrapper(); + } + } else { + // Plugins don't fall back if there is an error here. + if (mType == eType_Plugin) { + rv = NS_OK; // this is necessary to avoid auto-fallback + return NS_BINDING_ABORTED; + } + Fallback(false); + } + + return rv; + } + + Fallback(false); + return NS_BINDING_ABORTED; } NS_IMETHODIMP @@ -866,14 +1052,10 @@ nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest, } if (mFinalListener) { - return mFinalListener->OnDataAvailable(aRequest, aContext, aInputStream, - aOffset, aCount); + return mFinalListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); } - // We shouldn't have a connected channel with no final listener - NS_NOTREACHED("Got data for channel with no connected final listener"); - mChannel = nsnull; - + // Abort this load if we have no listener here return NS_ERROR_UNEXPECTED; } @@ -881,7 +1063,8 @@ nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest, NS_IMETHODIMP nsObjectLoadingContent::GetFrameLoader(nsIFrameLoader** aFrameLoader) { - NS_IF_ADDREF(*aFrameLoader = mFrameLoader); + *aFrameLoader = mFrameLoader; + NS_IF_ADDREF(*aFrameLoader); return NS_OK; } @@ -899,6 +1082,7 @@ nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader) return NS_ERROR_NOT_IMPLEMENTED; } +// nsIObjectLoadingContent NS_IMETHODIMP nsObjectLoadingContent::GetActualType(nsACString& aType) { @@ -916,25 +1100,24 @@ nsObjectLoadingContent::GetDisplayedType(PRUint32* aType) NS_IMETHODIMP nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) { - if (mType == eType_Plugin) { - if (!mInstanceOwner) { - // We have successfully set ourselves up in LoadObject, but not spawned an - // instance due to a lack of a frame. - AsyncStartPluginInstance(); - return NS_OK; - } - - // Disconnect any existing frame - DisconnectFrame(); - - // Set up relationship between instance owner and frame. - nsObjectFrame *objFrame = static_cast(aFrame); - mInstanceOwner->SetFrame(objFrame); - - // Set up new frame to draw. - objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size()); - objFrame->Invalidate(objFrame->GetContentRectRelativeToSelf()); + // Not having an instance yet is OK, but try to start one now that + // we have a frame. + if (!mInstanceOwner) { + AsyncStartPluginInstance(); + return NS_OK; } + + // Disconnect any existing frame + DisconnectFrame(); + + // Set up relationship between instance owner and frame. + nsObjectFrame *objFrame = static_cast(aFrame); + mInstanceOwner->SetFrame(objFrame); + + // Set up new frame to draw. + objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size()); + objFrame->Invalidate(objFrame->GetContentRectRelativeToSelf()); + return NS_OK; } @@ -1014,825 +1197,348 @@ nsObjectLoadingContent::ObjectState() const // plugins. return nsEventStates(); case eType_Null: - switch (mFallbackType) { - case eFallbackSuppressed: - return NS_EVENT_STATE_SUPPRESSED; - case eFallbackUserDisabled: - return NS_EVENT_STATE_USERDISABLED; - case eFallbackClickToPlay: + if (mSuppressed) + return NS_EVENT_STATE_SUPPRESSED; + if (mUserDisabled) + return NS_EVENT_STATE_USERDISABLED; + + // Otherwise, broken + nsEventStates state = NS_EVENT_STATE_BROKEN; + switch (mFallbackReason) { + case ePluginClickToPlay: return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY; - case eFallbackDisabled: - return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED; - case eFallbackBlocklisted: - return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED; - case eFallbackCrashed: - return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED; - case eFallbackUnsupported: - return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED; - case eFallbackOutdated: - case eFallbackAlternate: - return NS_EVENT_STATE_BROKEN; - case eFallbackVulnerableUpdatable: + case ePluginVulnerableUpdatable: return NS_EVENT_STATE_VULNERABLE_UPDATABLE; - case eFallbackVulnerableNoUpdate: + case ePluginVulnerableNoUpdate: return NS_EVENT_STATE_VULNERABLE_NO_UPDATE; + case ePluginDisabled: + state |= NS_EVENT_STATE_HANDLER_DISABLED; + break; + case ePluginBlocklisted: + state |= NS_EVENT_STATE_HANDLER_BLOCKED; + break; + case ePluginCrashed: + state |= NS_EVENT_STATE_HANDLER_CRASHED; + break; + case ePluginUnsupported: + state |= NS_EVENT_STATE_TYPE_UNSUPPORTED; + break; + case ePluginOutdated: + case ePluginOtherState: + // Do nothing, but avoid a compile warning + break; } + return state; }; NS_NOTREACHED("unknown type?"); - return NS_EVENT_STATE_LOADING; + // this return statement only exists to avoid a compile warning + return nsEventStates(); } -// Helper to call CheckURILoad on URI -> BaseURI and BaseURI -> Origin -bool nsObjectLoadingContent::CheckObjectURIs(PRInt16 *aContentPolicy, - PRInt32 aContentPolicyType) -{ - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "Must be an instance of content"); - - nsCOMPtr docBaseURI = thisContent->GetBaseURI(); - - // Must have these to load - if (!aContentPolicy || !mBaseURI) { - return false; - } - - bool ret; - if (!URIEquals(mBaseURI, docBaseURI)) { - // If our object sets a new baseURI, make sure that base URI could be - // loaded by the document - ret = CheckURILoad(mBaseURI, aContentPolicy, aContentPolicyType); - if (!ret) { - return false; - } - } - - if (mURI) { - return CheckURILoad(mURI, aContentPolicy, aContentPolicyType); - } - - return true; -} - -bool nsObjectLoadingContent::CheckURILoad(nsIURI *aURI, - PRInt16 *aContentPolicy, - PRInt32 aContentPolicyType) -{ - nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); - NS_ASSERTION(secMan, "No security manager!?"); - - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "Must be an instance of content"); - - nsCOMPtr docBaseURI = thisContent->GetBaseURI(); - - nsIDocument* doc = thisContent->OwnerDoc(); - nsresult rv = - secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), aURI, 0); - - if (NS_FAILED(rv)) { - nsCAutoString uri; - nsCAutoString baseUri; - aURI->GetSpec(uri); - aURI->GetSpec(baseUri); - LOG(("OBJLC [%p]: CheckLoadURIWithPrincipal denied load of %s (base %s)", - this, uri.get(), baseUri.get())); - return false; - } - - PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; // default permit - rv = NS_CheckContentLoadPolicy(aContentPolicyType, - aURI, - doc->NodePrincipal(), - static_cast(this), - mContentType, - nsnull, //extra - &shouldLoad, - nsContentUtils::GetContentPolicy(), - secMan); - NS_ENSURE_SUCCESS(rv, false); - if (aContentPolicy) { - *aContentPolicy = shouldLoad; - } - if (NS_CP_REJECTED(shouldLoad)) { - nsCAutoString uri; - nsCAutoString baseUri; - aURI->GetSpec(uri); - aURI->GetSpec(baseUri); - LOG(("OBJLC [%p]: Content policy denied load of %s (base %s)", - this, uri.get(), baseUri.get())); - return false; - } - return true; -} - -nsObjectLoadingContent::ParameterUpdateFlags -nsObjectLoadingContent::UpdateObjectParameters() -{ - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "Must be an instance of content"); - - PRUint32 caps = GetCapabilities(); - LOG(("OBJLC [%p]: Updating object parameters", this)); - - nsresult rv; - nsCAutoString newMime; - nsCOMPtr newURI; - nsCOMPtr newBaseURI; - ObjectType newType; - // Set if this state can't be used to load anything, forces eType_Null - bool stateInvalid = false; - // Indicates what parameters changed. - // eParamChannelChanged - means parameters that affect channel opening - // decisions changed - // eParamStateChanged - means anything that affects what content we load - // changed, even if the channel we'd open remains the - // same. - // - // State changes outside of the channel parameters only matter if we've - // already opened a channel or tried to instantiate content, whereas channel - // parameter changes require re-opening the channel even if we haven't gotten - // that far. - nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange; - - /// - /// Initial MIME Type - /// - if (thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) { - newMime.AssignLiteral("application/x-java-vm"); - } else { - nsAutoString typeAttr; - thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, typeAttr); - if (!typeAttr.IsEmpty()) { - CopyUTF16toUTF8(typeAttr, newMime); - } - } - - /// - /// classID - /// - - bool usingClassID = false; - if (caps & eSupportClassID) { - nsAutoString classIDAttr; - thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr); - if (!classIDAttr.IsEmpty()) { - usingClassID = true; - if (NS_FAILED(TypeForClassID(classIDAttr, newMime))) { - // XXX(johns): Our de-facto behavior since forever was to refuse to load - // Objects who don't have a classid we support, regardless of other type - // or uri info leads to a valid plugin. - newMime.Assign(""); - stateInvalid = true; - } - } - } - - /// - /// Codebase - /// - - nsAutoString codebaseStr; - nsCOMPtr docBaseURI = thisContent->GetBaseURI(); - thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebaseStr); - if (codebaseStr.IsEmpty() && thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) { - // bug 406541 - // NOTE we send the full absolute URI resolved here to java in - // pluginInstanceOwner to avoid disagreements between parsing of - // relative URIs. We need to mimic java's quirks here to make that - // not break things. - codebaseStr.AssignLiteral("/"); // Java resolves codebase="" as "/" - // XXX(johns) This doesn't catch the case of "file:" which java would - // interpret as "file:///" but we would interpret as this document's URI - // but with a changed scheme. - } - - if (!codebaseStr.IsEmpty()) { - rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newBaseURI), - codebaseStr, - thisContent->OwnerDoc(), - docBaseURI); - if (NS_SUCCEEDED(rv)) { - NS_TryToSetImmutable(newBaseURI); - } else { - // Malformed URI - LOG(("OBJLC [%p]: Could not parse plugin's codebase as a URI, " - "will use document baseURI instead", this)); - } - } - - // Otherwise, use normal document baseURI - if (!newBaseURI) { - newBaseURI = docBaseURI; - } - - /// - /// URI - /// - - nsAutoString uriStr; - // Different elements keep this in various locations - if (thisContent->NodeInfo()->Equals(nsGkAtoms::object)) { - thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, uriStr); - } else if (thisContent->NodeInfo()->Equals(nsGkAtoms::embed)) { - thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, uriStr); - } else if (thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) { - // Applet tags have no URI, and their 'code=' parameter is not parsed as one - } else { - NS_NOTREACHED("Unrecognized plugin-loading tag"); - } - - // Note that the baseURI changing could affect the newURI, even if uriStr did - // not change. - if (!uriStr.IsEmpty()) { - rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newURI), - uriStr, - thisContent->OwnerDoc(), - newBaseURI); - if (NS_SUCCEEDED(rv)) { - NS_TryToSetImmutable(newURI); - } else { - stateInvalid = true; - } - } - - /// - /// Check if the original (pre-channel) content-type or URI changed, and - /// record mOriginal{ContentType,URI} - /// - - if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) { - // These parameters changing requires re-opening the channel, so don't - // consider the currently-open channel below - // XXX(johns): Changing the mime type might change our decision on whether - // or not we load a channel, so we count changes to it as a - // channel parameter change for the sake of simplicity. - retval = (ParameterUpdateFlags)(retval | eParamChannelChanged); - LOG(("OBJLC [%p]: Channel parameters changed", this)); - } - mOriginalContentType = newMime; - mOriginalURI = newURI; - - /// - /// If we have a channel, see if its MIME type should take precendence and - /// check the final (redirected) URL - /// - - // If we have a loaded channel and channel parameters did not change, use it - // to determine what we would load. - bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged); - if (mChannel && useChannel) { - nsCString channelType; - rv = mChannel->GetContentType(channelType); - if (NS_FAILED(rv)) { - NS_NOTREACHED("GetContentType failed"); - stateInvalid = true; - channelType.Assign(""); - } - - LOG(("OBJLC [%p]: Channel has a content type of %s", this, channelType.get())); - - bool binaryChannelType = false; - if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) { - channelType = APPLICATION_OCTET_STREAM; - mChannel->SetContentType(channelType); - binaryChannelType = true; - } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) - || channelType.EqualsASCII(BINARY_OCTET_STREAM)) { - binaryChannelType = true; - } - - // Channel can change our URI through redirection - rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI)); - if (NS_FAILED(rv)) { - NS_NOTREACHED("NS_GetFinalChannelURI failure"); - stateInvalid = true; - } - - // The channel type overrides the guessed / provided type, except when: - // - // 1) If the channel returns a binary stream type, and we have a type hint - // for a non-document (we never want to display binary-as-document), - // use our type hint instead. - // 2) Our type hint is a type that we support with a plugin, ignore the - // server's type - // - // XXX(johns): HTML5's "typesmustmatch" attribute would need to be - // honored here if implemented - - ObjectType typeHint = newMime.IsEmpty() ? eType_Null : GetTypeOfContent(newMime); - - bool caseOne = binaryChannelType - && typeHint != eType_Null - && typeHint != eType_Document; - bool caseTwo = typeHint == eType_Plugin; - if (caseOne || caseTwo) { - // 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(newMime, typeHint, dummy); - if (!typeHint.IsEmpty()) { - mChannel->SetContentType(typeHint); - } - } else if (binaryChannelType - && IsPluginEnabledByExtension(newURI, newMime)) { - mChannel->SetContentType(newMime); - } else { - newMime = channelType; - } - } - - bool isJava = nsPluginHost::IsJavaMIMEType(newMime.get()); - if (useChannel && (!mChannel || isJava)) { - // Sanity checks - // - Java does not load with a channel, and being java retroactively changes - // how we may have interpreted the codebase to construct this URI above. - // Because the behavior here is more or less undefined, play it safe and - // reject the load. - // - // - (useChannel && !mChannel) is true if a channel was opened but was - // subsequently invalidated - // in that case. - stateInvalid = true; - } - - /// - /// Determine final type - /// - // 1) If we have attempted channel load, or set stateInvalid above, the type - // is always null (fallback) - // 2) Otherwise, If we have a loaded channel, we grabbed its mimeType above, - // use that type. - // 3) Otherwise, See if we can load this as a plugin without a channel - // (image/document types always need a channel). - // - If we have indication this is a plugin (mime, extension, or classID) - // AND: - // - We have eAllowPluginSkipChannel OR - // - We have no URI in the first place OR - // - We're loading based on classID - // XXX(johns): Legacy behavior is to skip channel loading if we have - // a classID. I don't know why. - // 3) Otherwise, if we have a URI, set type to loading to indicate - // we'd need a channel to proceed. - // 4) Otherwise, type null to indicate unloadable content (fallback) - // - // XXX(johns): tags both support URIs and have - // eAllowPluginSkipChannel, meaning it is possible that we have a URI, but - // are not going to open a channel for it. The old objLC code did this (in a - // less obviously-intended way), so it's probably best not to change our - // behavior at this point. - // We ALSO skip channel loading for objects whose type is found by ClassID - // (We only support a tiny subset of classid: java and ActiveX, above) - // - - if (stateInvalid) { - newType = eType_Null; - } else if (useChannel) { - // If useChannel is set above, we considered it in setting newMime - newType = GetTypeOfContent(newMime); - LOG(("OBJLC [%p]: Using channel type", this)); - } else if (((caps & eAllowPluginSkipChannel) || !newURI || usingClassID) && - (GetTypeOfContent(newMime) == eType_Plugin)) { - newType = eType_Plugin; - LOG(("OBJLC [%p]: Skipping loading channel, type plugin", this)); - } else if (newURI) { - // We could potentially load this if we opened a channel on mURI, indicate - // This by leaving type as loading - newType = eType_Loading; - } else { - // Unloadable - no URI, and no plugin type. Non-plugin types (images, - // documents) always load with a channel. - newType = eType_Null; - } - - /// - /// Update changed values - /// - - if (newType != mType) { - retval = (ParameterUpdateFlags)(retval | eParamStateChanged); - LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType)); - mType = newType; - } - - if (!URIEquals(mBaseURI, newBaseURI)) { - if (isJava) { - // Java bases its class loading on the base URI, so we consider the state - // to have changed if this changes. If the object is using a relative URI, - // mURI will have changed below regardless - retval = (ParameterUpdateFlags)(retval | eParamStateChanged); - } - LOG(("OBJLC [%p]: Object effective baseURI changed", this)); - mBaseURI = newBaseURI; - } - - if (!URIEquals(newURI, mURI)) { - retval = (ParameterUpdateFlags)(retval | eParamStateChanged); - LOG(("OBJLC [%p]: Object effective URI changed", this)); - mURI = newURI; - } - - if (mContentType != newMime) { - retval = (ParameterUpdateFlags)(retval | eParamStateChanged); - LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this, mContentType.get(), newMime.get())); - mContentType = newMime; - } - - return retval; -} - -// Only OnStartRequest should be passing the channel parameter +// nsresult -nsObjectLoadingContent::LoadObject(bool aNotify, +nsObjectLoadingContent::LoadObject(const nsAString& aURI, + bool aNotify, + const nsCString& aTypeHint, bool aForceLoad) { - return LoadObject(aNotify, aForceLoad, nsnull); -} + LOG(("OBJLC [%p]: Loading object: URI string=<%s> notify=%i type=<%s> forceload=%i\n", + this, NS_ConvertUTF16toUTF8(aURI).get(), aNotify, aTypeHint.get(), aForceLoad)); -nsresult -nsObjectLoadingContent::LoadObject(bool aNotify, - bool aForceLoad, - nsIRequest *aLoadingChannel) -{ - nsCOMPtr thisContent = + // Avoid StringToURI in order to use the codebase attribute as base URI + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); NS_ASSERTION(thisContent, "must be a content"); + nsIDocument* doc = thisContent->OwnerDoc(); - nsresult rv = NS_OK; + nsCOMPtr baseURI; + GetObjectBaseURI(thisContent, getter_AddRefs(baseURI)); - // XXX(johns): In these cases, we refuse to touch our content and just - // remain unloaded, as per legacy behavior. It would make more sense to - // load fallback content initially and refuse to ever change state again. - if (doc->IsBeingUsedAsImage() || doc->IsLoadedAsData()) { + nsCOMPtr uri; + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), + aURI, doc, + baseURI); + // If URI creation failed, fallback immediately - this only happens for + // malformed URIs + if (!uri) { + Fallback(aNotify); return NS_OK; } - // Sanity check - if (!thisContent->IsInDoc()) { - NS_NOTREACHED("LoadObject called while not bound to a document"); - return NS_ERROR_UNEXPECTED; + NS_TryToSetImmutable(uri); + + return LoadObject(uri, aNotify, aTypeHint, aForceLoad); +} + +void +nsObjectLoadingContent::UpdateFallbackState(nsIContent* aContent, + AutoFallback& fallback, + const nsCString& aTypeHint) +{ + // Notify the UI and update the fallback state + PluginSupportState state = GetPluginSupportState(aContent, aTypeHint); + if (state != ePluginOtherState) { + fallback.SetPluginState(state); + FirePluginError(aContent, state); } - - LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p", - this, aNotify, aForceLoad, aLoadingChannel)); - - // We can't re-use an already open channel, but aForceLoad may make us try - // to load a plugin without any changes in channel state. - if (aForceLoad && mChannelLoaded) { - CloseChannel(); - mChannelLoaded = false; - } - - // Save these for NotifyStateChanged(); - nsEventStates oldState = ObjectState(); - ObjectType oldType = mType; - - ParameterUpdateFlags stateChange = UpdateObjectParameters(); - - if (!stateChange && !aForceLoad) { - return NS_OK; - } - - /// - /// State has changed, unload existing content and attempt to load new type - /// - LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", - this, stateChange)); - - // Setup fallback info. We may also change type to fallback below in case of - // sanity/OOM/etc. errors. We default to showing alternate content - // NOTE LoadFallback can override this in some cases - FallbackType fallbackType = eFallbackAlternate; - - if (mType == eType_Null) { - nsresult pluginsupport = IsPluginEnabledForType(mContentType); - if (pluginsupport == NS_ERROR_PLUGIN_DISABLED) { - fallbackType = eFallbackDisabled; - } else if (pluginsupport == NS_ERROR_PLUGIN_BLOCKLISTED) { - fallbackType = eFallbackBlocklisted; - } else { - fallbackType = eFallbackUnsupported; - } - } - - // We synchronously start/stop plugin instances below, which may spin the - // event loop. Re-entering into the load is fine, but at that point the - // original load call needs to abort when unwinding - // NOTE this is located *after* the state change check, a subseqent load - // with no subsequently changed state will be a no-op. - mIsLoading = true; - AutoSetLoadingToFalse reentryCheck(this); - - // Unload existing content, keeping in mind stopping plugins might spin the - // event loop. Note that we check for still-open channels below - UnloadObject(false); // Don't reset state - if (!mIsLoading) { - // The event loop must've spun and re-entered into LoadObject, which - // finished the load - return NS_OK; - } - - if (stateChange & eParamChannelChanged) { - // If the channel params changed, throw away the channel, but unset - // mChannelLoaded so we'll still try to open a new one for this load if - // necessary - CloseChannel(); - mChannelLoaded = false; - } else if (mType == eType_Null && mChannel) { - // If we opened a channel but then failed to find a loadable state, throw it - // away. mChannelLoaded will indicate that we tried to load a channel at one - // point so we wont recurse - CloseChannel(); - } else if (mChannelLoaded && mChannel != aLoadingChannel) { - // The only time we should have a loaded channel with a changed state is - // when the channel has just opened -- in which case this call should - // have originated from OnStartRequest - NS_NOTREACHED("Loading with a channel, but state doesn't make sense"); - return NS_OK; - } - - // - // Security checks - // - - // NOTE For eType_Loading we'll try all three types, as we want to go ahead - // with the channel if it could be any acceptable type. This type is - // passed to OpenChannel() as the LoadType. We pass through LoadObject - // again once the channel is opened and we're actually loading, so if - // the final URI doesn't pass the now-known type, we'll abort. - PRInt32 policyType; - if (mType != eType_Null) { - bool allowLoad = false; - PRInt16 contentPolicy = nsIContentPolicy::ACCEPT; - PRUint32 caps = GetCapabilities(); - bool supportImage = caps & eSupportImages; - bool supportDoc = (caps & eSupportDocuments) || (caps & eSupportSVG); - bool supportPlugin = caps & eSupportPlugins; - if (mType == eType_Image || (mType == eType_Loading && supportImage)) { - policyType = nsIContentPolicy::TYPE_IMAGE; - allowLoad = CheckObjectURIs(&contentPolicy, policyType); - } - if (!allowLoad && - (mType == eType_Document || (mType == eType_Loading && supportDoc))) { - contentPolicy = nsIContentPolicy::ACCEPT; - policyType = nsIContentPolicy::TYPE_SUBDOCUMENT; - allowLoad = CheckObjectURIs(&contentPolicy, policyType); - } - if (!allowLoad && - (mType == eType_Plugin || (mType == eType_Loading && supportPlugin))) { - contentPolicy = nsIContentPolicy::ACCEPT; - policyType = nsIContentPolicy::TYPE_OBJECT; - allowLoad = CheckObjectURIs(&contentPolicy, policyType); - } - - // Load denied, switch to fallback and set disabled/suppressed if applicable - if (!allowLoad) { - mType = eType_Null; - if (contentPolicy == nsIContentPolicy::REJECT_TYPE) { - fallbackType = eFallbackUserDisabled; - } else { - fallbackType = eFallbackSuppressed; - } - } - } - - // If we're a plugin but shouldn't start yet, load fallback with - // reason click-to-play instead - FallbackType clickToPlayReason; - if (mType == eType_Plugin && !ShouldPlay(clickToPlayReason)) { - LOG(("OBJLC [%p]: Marking plugin as click-to-play", this)); - mType = eType_Null; - fallbackType = clickToPlayReason; - } - - // Sanity check: We shouldn't have any loaded resources, pending events, or - // a final listener at this point - if (mFrameLoader || mPendingInstantiateEvent || mInstanceOwner || - mFinalListener) - { - NS_NOTREACHED("Trying to load new plugin with existing content"); - rv = NS_ERROR_UNEXPECTED; - return NS_OK; - } - - // More sanity-checking: - // If mChannel is set, mChannelLoaded should be set, and vice-versa - if (mType != eType_Null && !!mChannel != mChannelLoaded) { - NS_NOTREACHED("Trying to load with bad channel state"); - rv = NS_ERROR_UNEXPECTED; - return NS_OK; - } - - /// - /// Attempt to load new type - /// - switch (mType) { - case eType_Image: - if (!mChannel) { - // We have a LoadImage() call, but UpdateObjectParameters requires a - // channel for images, so this is not a valid state. - NS_NOTREACHED("Attempting to load image without a channel?"); - rv = NS_ERROR_UNEXPECTED; - break; - } - rv = LoadImageWithChannel(mChannel, getter_AddRefs(mFinalListener)); - if (mFinalListener) { - // Note that LoadObject is called from mChannel's OnStartRequest - // when loading with a channel - mSrcStreamLoading = true; - rv = mFinalListener->OnStartRequest(mChannel, nsnull); - mSrcStreamLoading = false; - } - break; - case eType_Plugin: - { - if (mChannel) { - nsRefPtr pluginHost = - already_AddRefed(nsPluginHost::GetInst()); - if (!pluginHost) { - NS_NOTREACHED("No pluginHost"); - rv = NS_ERROR_UNEXPECTED; - break; - } - - // Force a sync state change now, we need the frame created - NotifyStateChanged(oldType, oldState, true, aNotify); - oldType = mType; - oldState = ObjectState(); - - rv = pluginHost->NewEmbeddedPluginStreamListener(mURI, this, nsnull, - getter_AddRefs(mFinalListener)); - if (NS_SUCCEEDED(rv)) { - // Note that LoadObject is called from mChannel's OnStartRequest - // when loading with a channel - - mSrcStreamLoading = true; - rv = mFinalListener->OnStartRequest(mChannel, nsnull); - mSrcStreamLoading = false; - if (NS_SUCCEEDED(rv)) { - NotifyContentObjectWrapper(); - } - } - } else { - rv = AsyncStartPluginInstance(); - } - } - break; - case eType_Document: - { - if (!mChannel) { - // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters - // requires documents have a channel, so this is not a valid state. - NS_NOTREACHED("Attempting to load a document without a channel"); - mType = eType_Null; - break; - } - if (!mFrameLoader) { - // Force a sync state change, we need the frame created - NotifyStateChanged(oldType, oldState, true, aNotify); - oldType = mType; - oldState = ObjectState(); - - mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(), - mNetworkCreated); - if (!mFrameLoader) { - NS_NOTREACHED("nsFrameLoader::Create failed"); - mType = eType_Null; - break; - } - } - - rv = mFrameLoader->CheckForRecursiveLoad(mURI); - if (NS_FAILED(rv)) { - mType = eType_Null; - break; - } - - // We're loading a document, so we have to set LOAD_DOCUMENT_URI - // (especially important for firing onload) - nsLoadFlags flags = 0; - mChannel->GetLoadFlags(&flags); - flags |= nsIChannel::LOAD_DOCUMENT_URI; - mChannel->SetLoadFlags(flags); - - nsCOMPtr docShell; - rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell)); - if (NS_FAILED(rv)) { - NS_NOTREACHED("Could not get DocShell from mFrameLoader?"); - mType = eType_Null; - break; - } - - nsCOMPtr req(do_QueryInterface(docShell)); - NS_ASSERTION(req, "Docshell must be an ifreq"); - - nsCOMPtr - uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv)); - if (NS_FAILED(rv)) { - NS_NOTREACHED("Failed to get uriLoader service"); - mType = eType_Null; - break; - } - rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req, - getter_AddRefs(mFinalListener)); - if (NS_SUCCEEDED(rv)) { - // Note that LoadObject is called from mChannel's OnStartRequest - // when loading with a channel - mSrcStreamLoading = true; - rv = mFinalListener->OnStartRequest(mChannel, nsnull); - mSrcStreamLoading = false; - } - } - break; - case eType_Loading: - // If our type remains Loading, we need a channel to proceed - rv = OpenChannel(policyType); - if (NS_FAILED(rv)) { - LOG(("OBJLC [%p]: OpenChannel returned failure (%u)", this, rv)); - } - break; - case eType_Null: - // Handled below, silence compiler warnings - break; - }; - - if (NS_FAILED(rv)) { - // If we failed in the loading hunk above, switch to fallback - LOG(("OBJLC [%p]: Loading failed, switching to fallback", this)); - mType = eType_Null; - } - - // Switching to fallback state - if (mType == eType_Null) { - LOG(("OBJLC [%p]: Loading fallback, type %u", this, fallbackType)); - NS_ASSERTION(!mFrameLoader && !mInstanceOwner, - "switched to type null but also loaded something"); - - if (mChannel) { - // If we were loading with a channel but then failed over, throw it away - // (this also closes mFinalListener) - CloseChannel(); - } - - // Don't notify or send events - we'll handle those ourselves - // (so really this is just setting mFallbackType) - LoadFallback(fallbackType, false); - } - - // Notify of our final state if we haven't already - NotifyStateChanged(oldType, oldState, false, aNotify); - - if (mType == eType_Null && mFallbackType != eFallbackAlternate) { - // if we're not showing alternate content, fire a pluginerror to trigger - // (we stopped LoadFallback from doing so above, it doesn't know of our old - // state) - FirePluginError(mFallbackType); - } - - return NS_OK; } nsresult -nsObjectLoadingContent::CloseChannel() +nsObjectLoadingContent::LoadObject(nsIURI* aURI, + bool aNotify, + const nsCString& aTypeHint, + bool aForceLoad) { + // Only do a URI equality check for things that aren't stopped plugins. + // This is because we still need to load again if the plugin has been stopped. + if (mType == eType_Document || mType == eType_Image || mInstanceOwner) { + if (mURI && aURI) { + bool equal; + nsresult rv = mURI->Equals(aURI, &equal); + if (NS_SUCCEEDED(rv) && equal && !aForceLoad) { + // URI didn't change, do nothing + return NS_OK; + } + StopPluginInstance(); + } + } + + // Need to revoke any potentially pending instantiate events + if (mType == eType_Plugin && mPendingInstantiateEvent) { + mPendingInstantiateEvent = nsnull; + } + + AutoNotifier notifier(this, aNotify); + + mUserDisabled = mSuppressed = false; + + mURI = aURI; + mContentType = aTypeHint; + + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); + + nsIDocument* doc = thisContent->OwnerDoc(); + if (doc->IsBeingUsedAsImage()) { + return NS_OK; + } + + // From here on, we will always change the content. This means that a + // possibly-loading channel should be aborted. if (mChannel) { - LOG(("OBJLC [%p]: Closing channel\n", this)); + LOG(("OBJLC [%p]: Cancelling existing load\n", this)); + // These three statements are carefully ordered: // - onStopRequest should get a channel whose status is the same as the // status argument // - onStopRequest must get a non-null channel mChannel->Cancel(NS_BINDING_ABORTED); if (mFinalListener) { - // NOTE mFinalListener is only created when we load with a channel, which - // LoadObject() requires come from a OnStartRequest call + // NOTE: Since mFinalListener is only set in onStartRequest, which takes + // care of calling mFinalListener->OnStartRequest, mFinalListener is only + // non-null here if onStartRequest was already called. mFinalListener->OnStopRequest(mChannel, nsnull, NS_BINDING_ABORTED); mFinalListener = nsnull; } mChannel = nsnull; } - return NS_OK; -} -nsresult -nsObjectLoadingContent::OpenChannel(PRInt32 aPolicyType) -{ - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "must be a content"); - nsIDocument* doc = thisContent->OwnerDoc(); - NS_ASSERTION(doc, "No owner document?"); - NS_ASSERTION(!mInstanceOwner && !mInstantiating, - "opening a new channel with already loaded content"); + // Security checks + if (doc->IsLoadedAsData()) { + if (!doc->IsStaticDocument()) { + Fallback(false); + } + return NS_OK; + } - nsresult rv; - mChannel = nsnull; + // Can't do security checks without a URI - hopefully the plugin will take + // care of that + // Null URIs happen when the URL to load is specified via other means than the + // data/src attribute, for example via custom elements. + if (aURI) { + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + NS_ASSERTION(secMan, "No security manager!?"); + nsresult rv = + secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), aURI, 0); + if (NS_FAILED(rv)) { + Fallback(false); + return NS_OK; + } + + PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; // default permit + rv = + NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT, + aURI, + doc->NodePrincipal(), + static_cast(this), + aTypeHint, + nsnull, //extra + &shouldLoad, + nsContentUtils::GetContentPolicy(), + secMan); + if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { + HandleBeingBlockedByContentPolicy(rv, shouldLoad); + return NS_OK; + } + } + + nsresult rv = NS_ERROR_UNEXPECTED; + // This fallback variable MUST be declared after the notifier variable. Do NOT + // change the order of the declarations! + AutoFallback fallback(this, &rv); + + PRUint32 caps = GetCapabilities(); + LOG(("OBJLC [%p]: Capabilities: %04x\n", this, caps)); + + nsCAutoString overrideType; + if ((caps & eOverrideServerType) && + ((!aTypeHint.IsEmpty() && NS_SUCCEEDED(IsPluginEnabledForType(aTypeHint))) || + (aURI && IsPluginEnabledByExtension(aURI, overrideType)))) { + ObjectType newType; + if (overrideType.IsEmpty()) { + newType = GetTypeOfContent(aTypeHint); + } else { + mContentType = overrideType; + newType = eType_Plugin; + } + + if (newType != mType) { + LOG(("OBJLC [%p]: (eOverrideServerType) Changing type from %u to %u\n", this, mType, newType)); + + UnloadContent(); + + // Must have a frameloader before creating a frame, or the frame will + // create its own. + if (!mFrameLoader && newType == eType_Document) { + mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(), + mNetworkCreated); + if (!mFrameLoader) { + mURI = nsnull; + return NS_OK; + } + } + + // Must notify here for plugins + // If aNotify is false, we'll just wait until we get a frame and use the + // async instantiate path. + // XXX is this still needed? (for documents?) + mType = newType; + if (aNotify) + notifier.Notify(); + } + switch (newType) { + case eType_Image: + // Don't notify, because we will take care of that ourselves. + if (aURI) { + rv = LoadImage(aURI, aForceLoad, false); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } + break; + case eType_Plugin: + rv = AsyncStartPluginInstance(); + break; + case eType_Document: + if (aURI) { + rv = mFrameLoader->LoadURI(aURI); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } + break; + case eType_Loading: + NS_NOTREACHED("Should not have a loading type here!"); + case eType_Null: + // No need to load anything, notify of the failure. + UpdateFallbackState(thisContent, fallback, aTypeHint); + break; + }; + return NS_OK; + } + + // If the class ID specifies a supported plugin, or if we have no explicit URI + // but a type, immediately instantiate the plugin. + bool isSupportedClassID = false; + nsCAutoString typeForID; // Will be set iff isSupportedClassID == true + bool hasID = false; + if (caps & eSupportClassID) { + nsAutoString classid; + thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classid); + if (!classid.IsEmpty()) { + hasID = true; + isSupportedClassID = NS_SUCCEEDED(TypeForClassID(classid, typeForID)); + } + } + + if (hasID && !isSupportedClassID) { + // We have a class ID and it's unsupported. Fallback in that case. + rv = NS_ERROR_NOT_AVAILABLE; + return NS_OK; + } + + if (isSupportedClassID || + (!aURI && !aTypeHint.IsEmpty() && + 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. + mType = eType_Plugin; + + // At this point, the stored content type + // must be equal to our type hint. Similar, + // our URI must be the requested URI. + // (->Equals would suffice, but == is cheaper + // and handles NULL) + NS_ASSERTION(mContentType.Equals(aTypeHint), "mContentType wrong!"); + NS_ASSERTION(mURI == aURI, "mURI wrong!"); + + if (isSupportedClassID) { + // Use the classid's type + NS_ASSERTION(!typeForID.IsEmpty(), "Must have a real type!"); + mContentType = typeForID; + // XXX(biesi). The plugin instantiation code used to pass the base URI + // here instead of the plugin URI for instantiation via class ID, so I + // continue to do so. Why that is, no idea... + GetObjectBaseURI(thisContent, getter_AddRefs(mURI)); + if (!mURI) { + mURI = aURI; + } + } + + // rv is references by a stack-based object, need to assign here + rv = AsyncStartPluginInstance(); + + return rv; + } + + if (!aURI) { + // No URI and if we have got this far no enabled plugin supports the type + rv = NS_ERROR_NOT_AVAILABLE; + + // We should only notify the UI if there is at least a type to go on for + // finding a plugin to use, unless it's a supported image or document type. + if (!aTypeHint.IsEmpty() && GetTypeOfContent(aTypeHint) == eType_Null) { + UpdateFallbackState(thisContent, fallback, aTypeHint); + } + + return NS_OK; + } // E.g. mms:// - if (!mURI || !CanHandleURI(mURI)) { - return NS_ERROR_NOT_AVAILABLE; + if (!CanHandleURI(aURI)) { + if (aTypeHint.IsEmpty()) { + rv = NS_ERROR_NOT_AVAILABLE; + return NS_OK; + } + + if (NS_SUCCEEDED(IsPluginEnabledForType(aTypeHint))) { + mType = eType_Plugin; + } else { + rv = NS_ERROR_NOT_AVAILABLE; + // No plugin to load, notify of the failure. + UpdateFallbackState(thisContent, fallback, aTypeHint); + } + + return NS_OK; } nsCOMPtr group = doc->GetDocumentLoadGroup(); @@ -1844,9 +1550,9 @@ nsObjectLoadingContent::OpenChannel(PRInt32 aPolicyType) if (csp) { channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1"); channelPolicy->SetContentSecurityPolicy(csp); - channelPolicy->SetLoadType(aPolicyType); + channelPolicy->SetLoadType(nsIContentPolicy::TYPE_OBJECT); } - rv = NS_NewChannel(getter_AddRefs(chan), mURI, nsnull, group, this, + rv = NS_NewChannel(getter_AddRefs(chan), aURI, nsnull, group, this, nsIChannel::LOAD_CALL_CONTENT_SNIFFERS | nsIChannel::LOAD_CLASSIFY_URI, channelPolicy); @@ -1858,21 +1564,35 @@ nsObjectLoadingContent::OpenChannel(PRInt32 aPolicyType) httpChan->SetReferrer(doc->GetDocumentURI()); } + // MIME Type hint + if (!aTypeHint.IsEmpty()) { + nsCAutoString typeHint, dummy; + NS_ParseContentType(aTypeHint, typeHint, dummy); + if (!typeHint.IsEmpty()) { + chan->SetContentType(typeHint); + } + } + // Set up the channel's principal and such, like nsDocShell::DoURILoad does nsContentUtils::SetUpChannelOwner(thisContent->NodePrincipal(), - chan, mURI, true); + chan, aURI, true); nsCOMPtr scriptChannel = do_QueryInterface(chan); if (scriptChannel) { // Allow execution against our context if the principals match - scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL); + scriptChannel-> + SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL); } // AsyncOpen can fail if a file does not exist. + // Show fallback content in that case. rv = chan->AsyncOpen(this, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - LOG(("OBJLC [%p]: Channel opened", this)); - mChannel = chan; + if (NS_SUCCEEDED(rv)) { + LOG(("OBJLC [%p]: Channel opened.\n", this)); + + mChannel = chan; + mType = eType_Loading; + } return NS_OK; } @@ -1886,14 +1606,38 @@ nsObjectLoadingContent::GetCapabilities() const } void -nsObjectLoadingContent::DestroyContent() +nsObjectLoadingContent::Fallback(bool aNotify) +{ + AutoNotifier notifier(this, aNotify); + + UnloadContent(); +} + +void +nsObjectLoadingContent::RemovedFromDocument() { if (mFrameLoader) { + // XXX This is very temporary and must go away mFrameLoader->Destroy(); mFrameLoader = nsnull; + + // Clear the current URI, so that LoadObject doesn't think that we + // have already loaded the content. + mURI = nsnull; } - StopPluginInstance(); + // 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 */ @@ -1905,24 +1649,109 @@ nsObjectLoadingContent::Traverse(nsObjectLoadingContent *tmp, cb.NoteXPCOMChild(static_cast(tmp->mFrameLoader)); } -void -nsObjectLoadingContent::UnloadObject(bool aResetState) +// +/* static */ bool +nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest) { - // Don't notify in CancelImageRequests until we transition to a new loaded - // state + nsresult status; + nsresult rv = aRequest->GetStatus(&status); + if (NS_FAILED(rv) || NS_FAILED(status)) { + return false; + } + + // This may still be an error page or somesuch + nsCOMPtr httpChan(do_QueryInterface(aRequest)); + if (httpChan) { + bool success; + rv = httpChan->GetRequestSucceeded(&success); + if (NS_FAILED(rv) || !success) { + return false; + } + } + + // Otherwise, the request is successful + return true; +} + +/* static */ bool +nsObjectLoadingContent::CanHandleURI(nsIURI* aURI) +{ + nsCAutoString scheme; + if (NS_FAILED(aURI->GetScheme(scheme))) { + return false; + } + + nsIIOService* ios = nsContentUtils::GetIOService(); + if (!ios) + return false; + + nsCOMPtr handler; + ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)); + if (!handler) { + return false; + } + + nsCOMPtr extHandler = + do_QueryInterface(handler); + // We can handle this URI if its protocol handler is not the external one + return extHandler == nsnull; +} + +bool +nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType) +{ + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "must be a content"); + + nsresult rv; + nsCOMPtr info( + do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv)); + PRUint32 supported; + if (info) { + nsCOMPtr webNav; + nsIDocument* currentDoc = thisContent->GetCurrentDoc(); + if (currentDoc) { + webNav = do_GetInterface(currentDoc->GetScriptGlobalObject()); + } + rv = info->IsTypeSupported(aMimeType, webNav, &supported); + } + + if (NS_SUCCEEDED(rv)) { + if (supported == nsIWebNavigationInfo::UNSUPPORTED) { + // Try a stream converter + // NOTE: We treat any type we can convert from as a supported type. If a + // type is not actually supported, the URI loader will detect that and + // return an error, and we'll fallback. + nsCOMPtr convServ = + do_GetService("@mozilla.org/streamConverters;1"); + bool canConvert = false; + if (convServ) { + rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert); + } + + return NS_SUCCEEDED(rv) && canConvert; + } + + // Don't want to support plugins as documents + return supported != nsIWebNavigationInfo::PLUGIN; + } + + return false; +} + +void +nsObjectLoadingContent::UnloadContent() +{ + // Don't notify in CancelImageRequests. We do it ourselves. CancelImageRequests(false); if (mFrameLoader) { mFrameLoader->Destroy(); mFrameLoader = nsnull; } - - if (aResetState) { - CloseChannel(); - mType = eType_Loading; - } - - // This call should be last as it may re-enter - StopPluginInstance(); + mType = eType_Null; + mUserDisabled = mSuppressed = false; + mFallbackReason = ePluginOtherState; } void @@ -1931,9 +1760,9 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, bool aSync, bool aNotify) { - LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx)" - " (sync %i, notify %i)", this, aOldType, aOldState.GetInternalValue(), - mType, ObjectState().GetInternalValue(), aSync, aNotify)); + LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx) (sync=%i)\n", + this, aOldType, aOldState.GetInternalValue(), mType, + ObjectState().GetInternalValue(), aSync)); nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); @@ -1941,8 +1770,6 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, NS_ASSERTION(thisContent->IsElement(), "Not an element?"); - // XXX(johns): A good bit of the code below replicates UpdateState(true) - // Unfortunately, we do some state changes without notifying // (e.g. in Fallback when canceling image requests), so we have to // manually notify object state changes. @@ -1983,17 +1810,14 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, } } -void -nsObjectLoadingContent::FirePluginError(FallbackType aFallbackType) +/* static */ void +nsObjectLoadingContent::FirePluginError(nsIContent* thisContent, + PluginSupportState state) { - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "must be a content"); + LOG(("OBJLC []: Dispatching nsPluginErrorEvent for content %p\n", + thisContent)); - LOG(("OBJLC [%p]: Dispatching nsPluginErrorEvent for content %p\n", - this)); - - nsCOMPtr ev = new nsPluginErrorEvent(thisContent, aFallbackType); + nsCOMPtr ev = new nsPluginErrorEvent(thisContent, state); nsresult rv = NS_DispatchToCurrentThread(ev); if (NS_FAILED(rv)) { NS_WARNING("failed to dispatch nsPluginErrorEvent"); @@ -2003,20 +1827,16 @@ nsObjectLoadingContent::FirePluginError(FallbackType aFallbackType) nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType) { - if (aMIMEType.IsEmpty()) { - return eType_Null; - } - PRUint32 caps = GetCapabilities(); if ((caps & eSupportImages) && IsSupportedImage(aMIMEType)) { return eType_Image; } - // SVGs load as documents, but are their own capability bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml"); - bool supportType = isSVG ? eSupportSVG : eSupportDocuments; - if ((caps & supportType) && IsSupportedDocument(aMIMEType)) { + bool supportedSVG = isSVG && (caps & eSupportSVG); + if (((caps & eSupportDocuments) || supportedSVG) && + IsSupportedDocument(aMIMEType)) { return eType_Document; } @@ -2055,6 +1875,27 @@ nsObjectLoadingContent::TypeForClassID(const nsAString& aClassID, return NS_ERROR_NOT_AVAILABLE; } +void +nsObjectLoadingContent::GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI) +{ + // We want to use swap(); since this is just called from this file, + // we can assert this (callers use comptrs) + NS_PRECONDITION(*aURI == nsnull, "URI must be inited to zero"); + + // For plugins, the codebase attribute is the base URI + nsCOMPtr baseURI = thisContent->GetBaseURI(); + nsAutoString codebase; + thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, + codebase); + if (!codebase.IsEmpty()) { + nsContentUtils::NewURIWithDocumentCharset(aURI, codebase, + thisContent->OwnerDoc(), + baseURI); + } else { + baseURI.swap(*aURI); + } +} + nsObjectFrame* nsObjectLoadingContent::GetExistingFrame() { @@ -2064,6 +1905,94 @@ nsObjectLoadingContent::GetExistingFrame() return static_cast(objFrame); } +void +nsObjectLoadingContent::HandleBeingBlockedByContentPolicy(nsresult aStatus, + PRInt16 aRetval) +{ + // Must call UnloadContent first, as it overwrites + // mSuppressed/mUserDisabled. It also takes care of setting the type to + // eType_Null. + UnloadContent(); + if (NS_SUCCEEDED(aStatus)) { + if (aRetval == nsIContentPolicy::REJECT_TYPE) { + mUserDisabled = true; + } else if (aRetval == nsIContentPolicy::REJECT_SERVER) { + mSuppressed = true; + } + } +} + +PluginSupportState +nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent, + const nsCString& aContentType) +{ + if (!aContent->IsHTML()) { + return ePluginOtherState; + } + + if (aContent->Tag() == nsGkAtoms::embed || + aContent->Tag() == nsGkAtoms::applet) { + return GetPluginDisabledState(aContentType); + } + + bool hasAlternateContent = false; + + // Search for a child with a pluginurl name + for (nsIContent* child = aContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsHTML(nsGkAtoms::param)) { + if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, + NS_LITERAL_STRING("pluginurl"), eIgnoreCase)) { + return GetPluginDisabledState(aContentType); + } + } else if (!hasAlternateContent) { + hasAlternateContent = + nsStyleUtil::IsSignificantChild(child, true, false); + } + } + + PluginSupportState pluginDisabledState = GetPluginDisabledState(aContentType); + if (pluginDisabledState == ePluginClickToPlay || + pluginDisabledState == ePluginVulnerableUpdatable || + pluginDisabledState == ePluginVulnerableNoUpdate) { + return pluginDisabledState; + } else if (hasAlternateContent) { + return ePluginOtherState; + } else { + return pluginDisabledState; + } +} + +PluginSupportState +nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType) +{ + nsresult rv = IsPluginEnabledForType(aContentType); + if (rv == NS_ERROR_PLUGIN_DISABLED) { + return ePluginDisabled; + } + if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY) { + PRUint32 state; + nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); + nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); + if (pluginHost) { + rv = pluginHost->GetBlocklistStateForType(aContentType.get(), &state); + if (NS_SUCCEEDED(rv)) { + if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) { + return ePluginVulnerableUpdatable; + } else if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) { + return ePluginVulnerableNoUpdate; + } + } + } + return ePluginClickToPlay; + } + if (rv == NS_ERROR_PLUGIN_BLOCKLISTED) { + return ePluginBlocklisted; + } + return ePluginUnsupported; +} + void nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const { @@ -2101,19 +2030,10 @@ nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag, const nsAString& browserDumpID, bool submittedCrashReport) { - LOG(("OBJLC [%p]: Plugin Crashed, queuing crash event", this)); - NS_ASSERTION(mType == eType_Plugin, "PluginCrashed at non-plugin type"); - - // Instance is dead, clean up - mInstanceOwner = nsnull; - CloseChannel(); - - // Switch to fallback/crashed state, notify - LoadFallback(eFallbackCrashed, true); - - // send nsPluginCrashedEvent - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); + AutoNotifier notifier(this, true); + UnloadContent(); + mFallbackReason = ePluginCrashed; + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); // Note that aPluginTag in invalidated after we're called, so copy // out any data we need now. @@ -2122,13 +2042,12 @@ nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag, nsCAutoString pluginFilename; aPluginTag->GetFilename(pluginFilename); - nsCOMPtr ev = - new nsPluginCrashedEvent(thisContent, - pluginDumpID, - browserDumpID, - NS_ConvertUTF8toUTF16(pluginName), - NS_ConvertUTF8toUTF16(pluginFilename), - submittedCrashReport); + nsCOMPtr ev = new nsPluginCrashedEvent(thisContent, + pluginDumpID, + browserDumpID, + NS_ConvertUTF8toUTF16(pluginName), + NS_ConvertUTF8toUTF16(pluginFilename), + submittedCrashReport); nsresult rv = NS_DispatchToCurrentThread(ev); if (NS_FAILED(rv)) { NS_WARNING("failed to dispatch nsPluginCrashedEvent"); @@ -2152,7 +2071,7 @@ nsObjectLoadingContent::SyncStartPluginInstance() nsCOMPtr kungFuURIGrip(mURI); nsCString contentType(mContentType); - return InstantiatePluginInstance(); + return InstantiatePluginInstance(contentType.get(), mURI.get()); } NS_IMETHODIMP @@ -2202,7 +2121,7 @@ DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner, if (aDelayedStop && aInstanceOwner->MatchPluginName("Shockwave Flash")) return false; #endif - + // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524), // XStandard (bug 430219), CMISS Zinc (bug 429604). if (aDelayedStop @@ -2221,81 +2140,6 @@ DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner, return false; } -void -nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) { - nsEventStates oldState = ObjectState(); - ObjectType oldType = mType; - - NS_ASSERTION(!mInstanceOwner && !mFrameLoader && !mChannel, - "LoadFallback called with loaded content"); - - // - // Fixup mFallbackType - // - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - NS_ASSERTION(thisContent, "must be a content"); - - if (!thisContent->IsHTML()) { - // Don't let custom fallback handlers run outside HTML - LOG(("OBJLC [%p]: Non-HTML content, forcing eFallbackAlternate", this)); - aType = eFallbackAlternate; - } - - /// XXX(johns): This block is just mimicing legacy behavior, not any spec - // Check if we have any significant content (excluding param tags) OR a - // param named 'pluginUrl' - bool hasAlternateContent = false; - bool hasPluginUrl = false; - if (thisContent->Tag() == nsGkAtoms::object && - (aType == eFallbackUnsupported || - aType == eFallbackDisabled || - aType == eFallbackBlocklisted)) - { - for (nsIContent* child = thisContent->GetFirstChild(); - child; child = child->GetNextSibling()) - { - if (child->IsHTML(nsGkAtoms::param)) { - if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, - NS_LITERAL_STRING("pluginurl"), eIgnoreCase)) { - hasPluginUrl = true; - } - } else if (nsStyleUtil::IsSignificantChild(child, true, false)) { - hasAlternateContent = true; - } - } - - // Show alternate content if it exists, unless we have a 'pluginurl' param, - // in which case the missing-plugin fallback handler will want to handle - // it - if (hasAlternateContent && !hasPluginUrl) { - LOG(("OBJLC [%p]: Unsupported/disabled/blocked plugin has alternate " - "content, showing instead of custom handler", this)); - aType = eFallbackAlternate; - } - } - - mType = eType_Null; - mFallbackType = aType; - - // - // Notify & send events - // - if (!aNotify) { - return; // done - } - - NotifyStateChanged(oldType, oldState, false, true); - - if (mFallbackType != eFallbackCrashed && - mFallbackType != eFallbackAlternate) - { - // Alternate content doesn't trigger a pluginError, and nsPluginCrashedEvent - // is only handled by ::PluginCrashed - FirePluginError(mFallbackType); - } -} - void nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, bool aDelayedStop, @@ -2303,7 +2147,7 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, { // DoStopPlugin can process events and there may be pending InDocCheckEvent // events which can drop in underneath us and destroy the instance we are - // about to destroy unless we prevent that with the mPluginStopping flag. + // about to destroy unless we prevent that with the mIsStopping flag. // (aForcedReentry is only true from the callback of an earlier delayed stop) if (mIsStopping && !aForcedReentry) { return; @@ -2322,35 +2166,22 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, aInstanceOwner->HidePluginWindow(); #endif - nsRefPtr pluginHost = - already_AddRefed(nsPluginHost::GetInst()); - NS_ASSERTION(pluginHost, "No plugin host?"); - pluginHost->StopPluginInstance(inst); + 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); } - + aInstanceOwner->Destroy(); + mIsStopping = false; } NS_IMETHODIMP nsObjectLoadingContent::StopPluginInstance() { - // Prevents any pending plugin starts from running - mPendingInstantiateEvent = nsnull; - if (!mInstanceOwner) { return NS_OK; } - if (mChannel) { - // The plugin has already used data from this channel, we'll need to - // re-open it to handle instantiating again, even if we don't invalidate - // our loaded state. - /// XXX(johns): Except currently, we don't, just leaving re-opening channels - /// to plugins... - LOG(("OBJLC [%p]: StopPluginInstance - Closing used channel", this)); - CloseChannel(); - } - DisconnectFrame(); bool delayedStop = false; @@ -2363,7 +2194,7 @@ nsObjectLoadingContent::StopPluginInstance() if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) { if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) { delayedStop = true; - } + } } } #endif @@ -2378,40 +2209,39 @@ nsObjectLoadingContent::StopPluginInstance() void nsObjectLoadingContent::NotifyContentObjectWrapper() { - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); nsCOMPtr doc = thisContent->GetDocument(); if (!doc) return; - + nsIScriptGlobalObject *sgo = doc->GetScopeObject(); if (!sgo) return; - + nsIScriptContext *scx = sgo->GetContext(); if (!scx) return; - + JSContext *cx = 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); } @@ -2421,90 +2251,13 @@ nsObjectLoadingContent::PlayPlugin() if (!nsContentUtils::IsCallerChrome()) return NS_OK; - mActivated = true; - return LoadObject(true, true); + mCTPPlayable = true; + return LoadObject(mURI, true, mContentType, true); } NS_IMETHODIMP -nsObjectLoadingContent::GetActivated(bool *aActivated) +nsObjectLoadingContent::GetActivated(bool* aActivated) { - FallbackType reason; - *aActivated = ShouldPlay(reason); + *aActivated = mActivated; return NS_OK; } - -bool -nsObjectLoadingContent::ShouldPlay(FallbackType &aReason) -{ - // mActivated is true if we've been activated via PlayPlugin() (e.g. user has - // clicked through). Otherwise, only play if click-to-play is off or if page - // is whitelisted - - nsRefPtr pluginHost = - already_AddRefed(nsPluginHost::GetInst()); - - bool isCTP = pluginHost->IsPluginClickToPlayForType(mContentType.get()); - - if (!isCTP || mActivated) { - return true; - } - - aReason = eFallbackClickToPlay; - - // If plugin type is click-to-play and we have not been explicitly clicked. - // check if permissions lets this page bypass - (e.g. user selected 'Always - // play plugins on this page') - nsresult rv = NS_ERROR_UNEXPECTED; - - nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); - MOZ_ASSERT(thisContent); - nsIDocument* ownerDoc = thisContent->OwnerDoc(); - - nsCOMPtr window = ownerDoc->GetWindow(); - if (!window) { - return false; - } - nsCOMPtr topWindow; - rv = window->GetTop(getter_AddRefs(topWindow)); - NS_ENSURE_SUCCESS(rv, false); - nsCOMPtr topDocument; - rv = topWindow->GetDocument(getter_AddRefs(topDocument)); - NS_ENSURE_SUCCESS(rv, false); - nsCOMPtr topDoc = do_QueryInterface(topDocument); - nsIURI* topUri = topDoc->GetDocumentURI(); - - nsCOMPtr permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, false); - - bool allowPerm = false; - // For now we always say that the system principal uses click-to-play since - // that maintains current behavior and we have tests that expect this. - // What we really should do is disable plugins entirely in pages that use - // the system principal, i.e. in chrome pages. That way the click-to-play - // code here wouldn't matter at all. Bug 775301 is tracking this. - if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) { - PRUint32 permission; - rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(), - "plugins", - &permission); - NS_ENSURE_SUCCESS(rv, false); - allowPerm = permission == nsIPermissionManager::ALLOW_ACTION; - } - - PRUint32 state; - rv = pluginHost->GetBlocklistStateForType(mContentType.get(), &state); - NS_ENSURE_SUCCESS(rv, false); - - // Always c2p vulnerable plugins, regardless of permissions - if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) { - aReason = eFallbackVulnerableUpdatable; - return false; - } - if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) { - aReason = eFallbackVulnerableNoUpdate; - return false; - } - - return allowPerm; -} - diff --git a/content/base/src/nsObjectLoadingContent.h b/content/base/src/nsObjectLoadingContent.h index a029015f23dd..704bb4909fc9 100644 --- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -31,6 +31,36 @@ class AutoFallback; class AutoSetInstantiatingToFalse; class nsObjectFrame; +enum PluginSupportState { + ePluginUnsupported, // The plugin is not supported (e.g. not installed) + ePluginDisabled, // The plugin has been explicitly disabled by the user + ePluginBlocklisted, // The plugin is blocklisted and disabled + ePluginOutdated, // The plugin is considered outdated, but not disabled + ePluginOtherState, // Something else (e.g. uninitialized or not a plugin) + ePluginCrashed, + ePluginClickToPlay, // The plugin is disabled until the user clicks on it + ePluginVulnerableUpdatable, // The plugin is vulnerable (update available) + ePluginVulnerableNoUpdate // The plugin is vulnerable (no update available) +}; + +/** + * INVARIANTS OF THIS CLASS + * - mChannel is non-null between asyncOpen and onStopRequest (NOTE: Only needs + * to be valid until onStopRequest is called on mFinalListener, not + * necessarily until the channel calls onStopRequest on us) + * - mChannel corresponds to the channel that gets passed to the + * nsIRequestObserver/nsIStreamListener methods + * - mChannel can be cancelled and ODA calls will stop + * - mFinalListener is non-null (only) after onStartRequest has been called on + * it and before onStopRequest has been called on it + * (i.e. calling onStopRequest doesn't violate the nsIRequestObserver + * contract) + * - mFrameLoader is null while this node is not in a document (XXX this + * invariant only exists due to nsFrameLoader suckage and needs to go away) + * - mInstantiating is true while in LoadObject (it may be true in other + * cases as well). Only the function that set mInstantiating should trigger + * frame construction or notifications like ContentStatesChanged or flushes. + */ class nsObjectLoadingContent : public nsImageLoadingContent , public nsIStreamListener , public nsIFrameLoaderOwner @@ -38,9 +68,9 @@ class nsObjectLoadingContent : public nsImageLoadingContent , public nsIInterfaceRequestor , public nsIChannelEventSink { + friend class AutoNotifier; + friend class AutoFallback; friend class AutoSetInstantiatingToFalse; - friend class AutoSetLoadingToFalse; - friend class InDocCheckEvent; friend class nsStopPluginRunnable; friend class nsAsyncInstantiateEvent; @@ -48,33 +78,11 @@ class nsObjectLoadingContent : public nsImageLoadingContent // This enum's values must be the same as the constants on // nsIObjectLoadingContent enum ObjectType { - // Loading, type not yet known. We may be waiting for a channel to open. - eType_Loading = TYPE_LOADING, - // Content is a *non-svg* image - eType_Image = TYPE_IMAGE, - // Content is a plugin - eType_Plugin = TYPE_PLUGIN, - // Content is a subdocument, possibly SVG - eType_Document = TYPE_DOCUMENT, - // No content loaded (fallback). May be showing alternate content or - // a custom error handler - *including* click-to-play dialogs - eType_Null = TYPE_NULL - }; - enum FallbackType { - eFallbackUnsupported, // The content type is not supported (e.g. plugin - // not installed) - eFallbackAlternate, // Showing alternate content - eFallbackDisabled, // The plugin exists, but is disabled - eFallbackBlocklisted, // The plugin is blocklisted and disabled - eFallbackOutdated, // The plugin is considered outdated, but not - // disabled - eFallbackCrashed, // The plugin has crashed - eFallbackSuppressed, // Suppressed by security policy - eFallbackUserDisabled, // Blocked by content policy - eFallbackClickToPlay, // The plugin is disabled until the user clicks on - // it - eFallbackVulnerableUpdatable, // The plugin is vulnerable (update avail) - eFallbackVulnerableNoUpdate // The plugin is vulnerable (no update avail) + eType_Loading = TYPE_LOADING, ///< Type not yet known + eType_Image = TYPE_IMAGE, ///< This content is an image + eType_Plugin = TYPE_PLUGIN, ///< This content is a plugin + eType_Document = TYPE_DOCUMENT, ///< This is a document type (e.g. HTML) + eType_Null = TYPE_NULL ///< Type can't be handled }; nsObjectLoadingContent(); @@ -94,106 +102,110 @@ class nsObjectLoadingContent : public nsImageLoadingContent using nsImageLoadingContent::OnStopRequest; #endif + ObjectType Type() { return mType; } + /** - * Object state. This is a bitmask of NS_EVENT_STATEs epresenting the - * current state of the object. + * Object state. This is a bitmask consisting of a subset of + * NS_EVENT_STATE_BROKEN, NS_EVENT_STATE_USERDISABLED and + * NS_EVENT_STATE_SUPPRESSED representing the current state of the object. */ nsEventStates ObjectState() const; - ObjectType Type() { return mType; } - void SetIsNetworkCreated(bool aNetworkCreated) { mNetworkCreated = aNetworkCreated; } - /** - * Immediately instantiate a plugin instance. This is a no-op if - * mType != eType_Plugin or a plugin is already running. - */ - nsresult InstantiatePluginInstance(); + // Can flush layout. + nsresult InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI); - /** - * Notify this class the document state has changed - * Called by nsDocument so we may suspend plugins in inactive documents) - */ void NotifyOwnerDocumentActivityChanged(); - /** - * Used by pluginHost to know if we're loading with a channel, so it - * will not open its own. - */ bool SrcStreamLoading() { return mSrcStreamLoading; }; protected: /** - * Begins loading the object when called + * Load the object from the given URI. + * @param aURI The URI to load. + * @param aNotify If true, nsIDocumentObserver state change notifications + * will be sent as needed. + * @param aTypeHint MIME Type hint. Overridden by the server unless this + * class has the eOverrideServerType capability. + * @param aForceLoad If true, the object will be refetched even if the URI + * is the same as the currently-loaded object. + * @note Prefer the nsIURI-taking version of this function if a URI object + * is already available. + * @see the URI-taking version of this function for a detailed description + * of how a plugin will be found. + */ + nsresult LoadObject(const nsAString& aURI, + bool aNotify, + const nsCString& aTypeHint = EmptyCString(), + bool aForceLoad = false); + /** + * Loads the object from the given URI. * - * Attributes of |this| QI'd to nsIContent will be inspected, depending on - * the node type. This function currently assumes it is a , - * , or tag. + * The URI and type can both be null; if the URI is null a plugin will be + * instantiated in the hope that there is a with a useful URI + * somewhere around. Other attributes of |this| QI'd to nsIContent will be + * inspected. This function attempts hard to find a suitable plugin. * - * The instantiated plugin depends on: - * - The URI (, ) - * - The type 'hint' (type attribute) - * - The mime type returned by opening the URI - * - Enabled plugins claiming the ultimate mime type - * - The capabilities returned by GetCapabilities + * The instantiated plugin depends on three values: + * - The passed-in URI + * - The passed-in type hint * - The classid attribute, if eSupportClassID is among the capabilities + * and such an attribute is present.. * - * If eAllowPluginSkipChannel is true, we may skip opening the URI if our - * type hint points to a valid plugin, deferring that responsibility to the - * plugin. - * Similarly, if no URI is provided, but a type hint for a valid plugin is - * present, that plugin will be instantiated + * Supported class ID attributes override any other value. + * + * If no class ID is present and aForceType is true, the handler given by + * aTypeHint will be instantiated for this content. + * If the URI is null or not supported, and a type hint is given, the plugin + * corresponding to that type is instantiated. * * Otherwise a request to that URI is made and the type sent by the server - * is used to find a suitable handler, EXCEPT when: - * - The type hint refers to a *supported* plugin, in which case that - * plugin will be instantiated regardless of the server provided type - * - The server returns a binary-stream type, and our type hint refers to - * a valid non-document type, we will use the type hint + * is used to find a suitable handler. * - * @param aNotify If we should send notifications. If false, content - * loading may be deferred while appropriate frames are - * created - * @param aForceLoad If we should reload this content (and re-attempt the - * channel open) even if our parameters did not change + * @param aForceLoad If true, the object will be refetched even if the URI + * is the same as the currently-loaded object. */ - nsresult LoadObject(bool aNotify, + nsresult LoadObject(nsIURI* aURI, + bool aNotify, + const nsCString& aTypeHint = EmptyCString(), bool aForceLoad = false); enum Capabilities { - eSupportImages = PR_BIT(0), // Images are supported (imgILoader) - eSupportPlugins = PR_BIT(1), // Plugins are supported (nsIPluginHost) - eSupportDocuments = PR_BIT(2), // Documents are supported - // (nsIDocumentLoaderFactory) - // This flag always includes SVG - eSupportSVG = PR_BIT(3), // SVG is supported (image/svg+xml) - eSupportClassID = PR_BIT(4), // The classid attribute is supported - - // Allows us to load a plugin if it matches a MIME type or file extension - // registered to a plugin without opening its specified URI first. Can - // result in launching plugins for URIs that return differing content - // types. Plugins without URIs may instantiate regardless. - // XXX(johns) this is our legacy behavior on tags, whereas object - // will always open a channel and check its MIME if a URI is present. - eAllowPluginSkipChannel = PR_BIT(5) + eSupportImages = PR_BIT(0), // Images are supported (imgILoader) + eSupportPlugins = PR_BIT(1), // Plugins are supported (nsIPluginHost) + eSupportDocuments = PR_BIT(2), // Documents are supported + // (nsIDocumentLoaderFactory) + // This flag always includes SVG + eSupportSVG = PR_BIT(3), // SVG is supported (image/svg+xml) + eSupportClassID = PR_BIT(4), // The classid attribute is supported + eOverrideServerType = PR_BIT(5) // The server-sent MIME type is ignored + // (ignored if no type is specified) }; /** * Returns the list of capabilities this content node supports. This is a * bitmask consisting of flags from the Capabilities enum. * - * The default implementation supports all types but not - * eSupportClassID or eAllowPluginSkipChannel + * The default implementation supports all types but no classids. */ virtual PRUint32 GetCapabilities() const; /** - * Destroys all loaded documents/plugins and releases references + * Fall back to rendering the alternative content. */ - void DestroyContent(); + void Fallback(bool aNotify); + + /** + * Subclasses must call this function when they are removed from the + * document. + * + * XXX This is a temporary workaround for docshell suckyness + */ + void RemovedFromDocument(); static void Traverse(nsObjectLoadingContent *tmp, nsCycleCollectionTraversalCallback &cb); @@ -210,127 +222,30 @@ class nsObjectLoadingContent : public nsImageLoadingContent bool aNullParent = true); private: - // Object parameter changes returned by UpdateObjectParameters - enum ParameterUpdateFlags { - eParamNoChange = 0, - // Parameters that potentially affect the channel changed - // - mOriginalURI, mOriginalContentType - eParamChannelChanged = PR_BIT(0), - // Parameters that affect displayed content changed - // - mURI, mContentType, mType, mBaseURI - eParamStateChanged = PR_BIT(1) - }; - - /** - * Loads fallback content with the specified FallbackType - * - * @param aType FallbackType value for type of fallback we're loading - * @param aNotify Send notifications and events. If false, caller is - * responsible for doing so - */ - void LoadFallback(FallbackType aType, bool aNotify); - - /** - * Internal version of LoadObject that should only be used by this class - * aLoadingChannel is passed by the LoadObject call from OnStartRequest, - * primarily for sanity-preservation - */ - nsresult LoadObject(bool aNotify, - bool aForceLoad, - nsIRequest *aLoadingChannel); - - /** - * Introspects the object and sets the following member variables: - * - mOriginalContentType : This is the type attribute on the element - * - mOriginalURI : The src or data attribute on the element - * - mURI : The final URI, considering mChannel if - * mChannelLoaded is set - * - mContentType : The final content type, considering mChannel if - * mChannelLoaded is set - * - mBaseURI : The object's base URI, which may be set by the - * object (codebase attribute) - * - mType : The type the object is determined to be based - * on the above - * - * NOTE The class assumes that mType is the currently loaded type at various - * points, so the caller of this function must take the appropriate - * actions to ensure this - * - * NOTE This function does not perform security checks, only determining the - * requested type and parameters of the object. - * - * @return Returns a bitmask of ParameterUpdateFlags values - */ - ParameterUpdateFlags UpdateObjectParameters(); void NotifyContentObjectWrapper(); /** - * Opens the channel pointed to by mURI into mChannel. - * - * @param aPolicyType The value to be passed to channelPolicy->SetLoadType + * Check whether the given request represents a successful load. */ - nsresult OpenChannel(PRInt32 aPolicyType); + static bool IsSuccessfulRequest(nsIRequest* aRequest); /** - * Closes and releases references to mChannel and, if opened, mFinalListener + * Check whether the URI can be handled internally. */ - nsresult CloseChannel(); + static bool CanHandleURI(nsIURI* aURI); /** - * If this object is allowed to play plugin content, or if it would display - * click-to-play instead. - * NOTE that this does not actually check if the object is a loadable plugin - */ - bool ShouldPlay(FallbackType &aReason); - - /** - * Checks if a URI passes security checks and content policy, relative to - * the current document's principal - * - * @param aURI The URI to consider - * @param aContentPolicy [in/out] A pointer to the initial content - * policy, that will be updated to contain the - * final determined policy - * @param aContentPolicyType The 'contentType' parameter passed to - * NS_CheckContentLoadPolicy - * - * @return true if this URI is acceptable for loading - */ - bool CheckURILoad(nsIURI *aURI, - PRInt16 *aContentPolicy, - PRInt32 aContentPolicyType); - - /** - * Checks if the current mURI and mBaseURI pass content policy and security - * checks for loading - * - * @param aContentPolicy [in/out] A pointer to the initial content - * policy, that will be updated to contain the - * final determined policy if a URL is rejected - * @param aContentPolicyType The 'contentType' parameter passed to - * NS_CheckContentLoadPolicy - * - * @return true if the URIs are acceptable for loading - */ - bool CheckObjectURIs(PRInt16 *aContentPolicy, PRInt32 aContentPolicyType); - - /** - * Checks whether the given type is a supported document type - * - * NOTE Does not take content policy or capabilities into account + * Checks whether the given type is a supported document type. */ bool IsSupportedDocument(const nsCString& aType); /** - * Unloads all content and resets the object to a completely unloaded state - * - * NOTE Calls StopPluginInstance() and may spin the event loop - * - * @param aResetState Reset the object type to 'loading' and destroy channel - * as well + * Unload the currently loaded content. This removes all state related to + * the displayed content and sets the type to eType_Null. + * Note: This does not send any notifications. */ - void UnloadObject(bool aResetState = true); + void UnloadContent(); /** * Notifies document observes about a new type/state of this object. @@ -346,112 +261,141 @@ class nsObjectLoadingContent : public nsImageLoadingContent bool aSync, bool aNotify); /** - * Fires the nsPluginErrorEvent. This function doesn't do any checks - * whether it should be fired, or whether the given state translates to a - * meaningful event + * Fires the "Plugin not found" event. This function doesn't do any checks + * whether it should be fired, the caller should do that. */ - void FirePluginError(FallbackType aFallbackType); + static void FirePluginError(nsIContent* thisContent, PluginSupportState state); - /** - * Returns a ObjectType value corresponding to the type of content we would - * support the given MIME type as, taking capabilities and plugin state - * into account - * - * NOTE this does not consider whether the content would be suppressed by - * click-to-play or other content policy checks - */ ObjectType GetTypeOfContent(const nsCString& aMIMEType); /** * For a classid, returns the MIME type that can be used to instantiate * a plugin for this ID. * - * @param aClassID The class ID in question - * @param aType [out] The corresponding type, if the call is successful * @return NS_ERROR_NOT_AVAILABLE Unsupported class ID. */ nsresult TypeForClassID(const nsAString& aClassID, nsACString& aType); + /** + * Gets the base URI to be used for this object. This differs from + * nsIContent::GetBaseURI in that it takes codebase attributes into + * account. + */ + void GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI); + + /** * Gets the frame that's associated with this content node. * Does not flush. */ nsObjectFrame* GetExistingFrame(); - // The final listener for mChannel (uriloader, pluginstreamlistener, etc.) + /** + * Handle being blocked by a content policy. aStatus is the nsresult + * return value of the Should* call, while aRetval is what it returned in + * its out parameter. + */ + void HandleBeingBlockedByContentPolicy(nsresult aStatus, + PRInt16 aRetval); + + /** + * Get the plugin support state for the given content node and MIME type. + * This is used for purposes of determining whether to fire PluginNotFound + * events etc. aContentType is the MIME type we ended up with. + * + * This should only be called if the type of this content is eType_Null. + */ + PluginSupportState GetPluginSupportState(nsIContent* aContent, const nsCString& aContentType); + + /** + * If the plugin for aContentType is disabled, return ePluginDisabled. + * Otherwise (including if there is no plugin for aContentType at all), + * return ePluginUnsupported. + * + * This should only be called if the type of this content is eType_Null. + */ + PluginSupportState GetPluginDisabledState(const nsCString& aContentType); + + /** + * When there is no usable plugin available this will send UI events and + * update the AutoFallback object appropriate to the reason for there being + * no plugin available. + */ + void UpdateFallbackState(nsIContent* aContent, AutoFallback& fallback, const nsCString& aTypeHint); + + nsresult IsPluginEnabledForType(const nsCString& aMIMEType); + bool IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType); + + /** + * The final listener to ship the data to (imagelib, uriloader, etc) + */ nsCOMPtr mFinalListener; - // Frame loader, for content documents we load. + /** + * Frame loader, for content documents we load. + */ nsRefPtr mFrameLoader; - // A pending nsAsyncInstantiateEvent (may be null). This is a weak ref. + /** + * A pending nsAsyncInstantiateEvent (may be null). This is a weak ref. + */ nsIRunnable *mPendingInstantiateEvent; - // The content type of our current load target, updated by - // UpdateObjectParameters(). Takes the channel's type into account once - // opened. - // - // May change if a channel is opened, does not imply a loaded state + /** + * The content type of the resource we were last asked to load. + */ nsCString mContentType; - // The content type 'hint' provided by the element's type attribute. May - // or may not be used as a final type - nsCString mOriginalContentType; + /** + * The channel that's currently being loaded. This is a weak reference. + * Non-null between asyncOpen and onStopRequest. + */ + nsIChannel* mChannel; - // The channel that's currently being loaded. If set, but mChannelLoaded is - // false, has not yet reached OnStartRequest - nsCOMPtr mChannel; - - // The URI of the current content. - // May change as we open channels and encounter redirects - does not imply - // a loaded type + // The data we were last asked to load nsCOMPtr mURI; - // The original URI obtained from inspecting the element (codebase, and - // src/data). May differ from mURI due to redirects - nsCOMPtr mOriginalURI; + /** + * Type of the currently-loaded content. + */ + ObjectType mType : 16; - // The baseURI used for constructing mURI, and used by some plugins (java) - // as a root for other resource requests. - nsCOMPtr mBaseURI; - - - - // Type of the currently-loaded content. - ObjectType mType : 8; - // The type of fallback content we're showing (see ObjectState()) - FallbackType mFallbackType : 8; - - // If true, the current load has finished opening a channel. Does not imply - // mChannel -- mChannelLoaded && !mChannel may occur for a load that failed - bool mChannelLoaded : 1; - - // Whether we are about to call instantiate on our frame. If we aren't, - // SetFrame needs to asynchronously call Instantiate. + /** + * Whether we are about to call instantiate on our frame. If we aren't, + * SetFrame needs to asynchronously call Instantiate. + */ bool mInstantiating : 1; + // Blocking status from content policy + bool mUserDisabled : 1; + bool mSuppressed : 1; // True when the object is created for an element which the parser has // created using NS_FROM_PARSER_NETWORK flag. If the element is modified, // it may lose the flag. bool mNetworkCreated : 1; - // Used to keep track of whether or not a plugin has been explicitly - // activated by PlayPlugin(). (see ShouldPlay()) + // Used to keep track of if a plugin is blocked by click-to-play. + // True indicates the plugin is not click-to-play or it has been clicked by + // the user. + // False indicates the plugin is click-to-play and has not yet been clicked. + bool mCTPPlayable : 1; + + // Used to keep track of whether or not a plugin has been played. + // This is used for click-to-play plugins. bool mActivated : 1; // Protects DoStopPlugin from reentry (bug 724781). bool mIsStopping : 1; - // Protects LoadObject from re-entry - bool mIsLoading : 1; - // Used to track when we might try to instantiate a plugin instance based on - // a src data stream being delivered to this object. When this is true we - // don't want plugin instance instantiation code to attempt to load src data - // again or we'll deliver duplicate streams. Should be cleared when we are - // not loading src data. + // a src data stream being delivered to this object. When this is true we don't + // want plugin instance instantiation code to attempt to load src data again or + // we'll deliver duplicate streams. Should be cleared when we are not loading + // src data. bool mSrcStreamLoading; + // A specific state that caused us to fallback + PluginSupportState mFallbackReason; nsWeakFrame mPrintFrame; diff --git a/content/html/content/src/nsHTMLObjectElement.cpp b/content/html/content/src/nsHTMLObjectElement.cpp index 2c6c07046936..bdc68b9cb231 100644 --- a/content/html/content/src/nsHTMLObjectElement.cpp +++ b/content/html/content/src/nsHTMLObjectElement.cpp @@ -268,6 +268,7 @@ void nsHTMLObjectElement::UnbindFromTree(bool aDeep, bool aNullParent) { + RemovedFromDocument(); nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent); } @@ -279,12 +280,10 @@ nsHTMLObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName, nsIAtom *aPrefix, const nsAString &aValue, bool aNotify) { - nsresult rv = nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - // if aNotify is false, we are coming from the parser or some such place; - // we'll get bound after all the attributes have been set, so we'll do the + // If we plan to call LoadObject, we want to do it first so that the + // object load kicks off _before_ the reflow triggered by the SetAttr. But if + // aNotify is false, we are coming from the parser or some such place; we'll + // get bound after all the attributes have been set, so we'll do the // object load from BindToTree/DoneAddingChildren. // Skip the LoadObject call in that case. // We also don't want to start loading the object when we're not yet in @@ -292,27 +291,24 @@ nsHTMLObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName, // attributes before inserting the node into the document. if (aNotify && IsInDoc() && mIsDoneAddingChildren && aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) { - return LoadObject(aNotify, true); + nsAutoString type; + GetAttr(kNameSpaceID_None, nsGkAtoms::type, type); + LoadObject(aValue, aNotify, NS_ConvertUTF16toUTF8(type), true); } - return NS_OK; + return nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix, + aValue, aNotify); } nsresult nsHTMLObjectElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, bool aNotify) { - nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, - aAttribute, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - // See comment in SetAttr - if (aNotify && IsInDoc() && mIsDoneAddingChildren && - aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) { - return LoadObject(aNotify, true); + if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) { + Fallback(aNotify); } - return NS_OK; + return nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); } bool @@ -517,7 +513,20 @@ nsHTMLObjectElement::GetAttributeMappingFunction() const void nsHTMLObjectElement::StartObjectLoad(bool aNotify) { - LoadObject(aNotify); + nsAutoString type; + GetAttr(kNameSpaceID_None, nsGkAtoms::type, type); + NS_ConvertUTF16toUTF8 ctype(type); + + nsAutoString uri; + if (GetAttr(kNameSpaceID_None, nsGkAtoms::data, uri)) { + LoadObject(uri, aNotify, ctype); + } + else { + // Be sure to call the nsIURI version if we have no attribute + // That handles the case where no URI is specified. An empty string would + // get interpreted as the page itself, instead of absence of URI. + LoadObject(nsnull, aNotify, ctype); + } SetIsNetworkCreated(false); } @@ -536,7 +545,7 @@ nsHTMLObjectElement::GetCapabilities() const void nsHTMLObjectElement::DestroyContent() { - nsObjectLoadingContent::DestroyContent(); + RemovedFromDocument(); nsGenericHTMLFormElement::DestroyContent(); } diff --git a/content/html/content/src/nsHTMLSharedObjectElement.cpp b/content/html/content/src/nsHTMLSharedObjectElement.cpp index 2b5504d69991..8183b16d3e78 100644 --- a/content/html/content/src/nsHTMLSharedObjectElement.cpp +++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp @@ -298,22 +298,22 @@ void nsHTMLSharedObjectElement::UnbindFromTree(bool aDeep, bool aNullParent) { + RemovedFromDocument(); nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } + nsresult nsHTMLSharedObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName, nsIAtom *aPrefix, const nsAString &aValue, bool aNotify) { - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - // if aNotify is false, we are coming from the parser or some such place; - // we'll get bound after all the attributes have been set, so we'll do the + // If we plan to call LoadObject, we want to do it first so that the + // object load kicks off _before_ the reflow triggered by the SetAttr. But if + // aNotify is false, we are coming from the parser or some such place; we'll + // get bound after all the attributes have been set, so we'll do the // object load from BindToTree/DoneAddingChildren. // Skip the LoadObject call in that case. // We also don't want to start loading the object when we're not yet in @@ -321,10 +321,13 @@ nsHTMLSharedObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName, // attributes before inserting the node into the document. if (aNotify && IsInDoc() && mIsDoneAddingChildren && aNameSpaceID == kNameSpaceID_None && aName == URIAttrName()) { - return LoadObject(aNotify, true); + nsCAutoString type; + GetTypeAttrValue(type); + LoadObject(aValue, aNotify, type, true); } - return NS_OK; + return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, + aNotify); } bool @@ -465,7 +468,19 @@ nsHTMLSharedObjectElement::GetAttributeMappingFunction() const void nsHTMLSharedObjectElement::StartObjectLoad(bool aNotify) { - LoadObject(aNotify); + nsCAutoString type; + GetTypeAttrValue(type); + + nsAutoString uri; + if (!GetAttr(kNameSpaceID_None, URIAttrName(), uri)) { + // Be sure to call the nsIURI version if we have no attribute + // That handles the case where no URI is specified. An empty string would + // get interpreted as the page itself, instead of absence of URI. + LoadObject(nsnull, aNotify, type); + } + else { + LoadObject(uri, aNotify, type); + } SetIsNetworkCreated(false); } @@ -478,7 +493,7 @@ nsHTMLSharedObjectElement::IntrinsicState() const PRUint32 nsHTMLSharedObjectElement::GetCapabilities() const { - PRUint32 capabilities = eSupportPlugins | eAllowPluginSkipChannel; + PRUint32 capabilities = eSupportPlugins | eOverrideServerType; if (mNodeInfo->Equals(nsGkAtoms::embed)) { capabilities |= eSupportSVG | eSupportImages; } @@ -489,7 +504,7 @@ nsHTMLSharedObjectElement::GetCapabilities() const void nsHTMLSharedObjectElement::DestroyContent() { - nsObjectLoadingContent::DestroyContent(); + RemovedFromDocument(); nsGenericHTMLElement::DestroyContent(); } diff --git a/content/html/document/src/PluginDocument.cpp b/content/html/document/src/PluginDocument.cpp index 605370033e4f..2c52d78d1ac5 100644 --- a/content/html/document/src/PluginDocument.cpp +++ b/content/html/document/src/PluginDocument.cpp @@ -120,7 +120,8 @@ PluginStreamListener::SetupPlugin() return NS_ERROR_UNEXPECTED; } nsObjectLoadingContent* olcc = static_cast(olc.get()); - nsresult rv = olcc->InstantiatePluginInstance(); + nsresult rv = olcc->InstantiatePluginInstance(mPluginDoc->GetType().get(), + mDocument->nsIDocument::GetDocumentURI()); if (NS_FAILED(rv)) { return rv; } diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp index 0eecc29fcd24..a0856462b597 100644 --- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp +++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp @@ -601,7 +601,7 @@ nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request, // if we have a mime type now. if (!mPluginInstance && mContent && !aContentType.IsEmpty()) { nsObjectLoadingContent *olc = static_cast(mContent.get()); - rv = olc->InstantiatePluginInstance(); + rv = olc->InstantiatePluginInstance(aContentType.get(), aURL.get()); if (NS_SUCCEEDED(rv)) { rv = olc->GetPluginInstance(getter_AddRefs(mPluginInstance)); if (NS_FAILED(rv)) { diff --git a/toolkit/identity/tests/chrome/sandbox_content.html b/toolkit/identity/tests/chrome/sandbox_content.html index 9a9b63ac2426..bc41e2f3c812 100644 --- a/toolkit/identity/tests/chrome/sandbox_content.html +++ b/toolkit/identity/tests/chrome/sandbox_content.html @@ -21,8 +21,8 @@ - - + + diff --git a/toolkit/identity/tests/chrome/test_sandbox.xul b/toolkit/identity/tests/chrome/test_sandbox.xul index 2bf2840c0447..6b47db77ef92 100644 --- a/toolkit/identity/tests/chrome/test_sandbox.xul +++ b/toolkit/identity/tests/chrome/test_sandbox.xul @@ -119,7 +119,7 @@ function test_url_normalization() { /** * Check with the server's state to see what content was loaded then reset it. */ -function check_loaded_content(aSandbox, aNothingShouldLoad, aCallback) { +function check_loaded_content(aNothingShouldLoad, aCallback) { let xhr = new XMLHttpRequest(); xhr.open("GET", STATE_URL + "?get_loaded", true); @@ -129,32 +129,16 @@ function check_loaded_content(aSandbox, aNothingShouldLoad, aCallback) { if (aNothingShouldLoad) { is(xhr.responseText, "NOTHING", "Check that nothing was loaded on the server"); } else if (!OSX_10_5) { - let allowedTypes = [ "application/javascript", "text/html", "video/webm", - "audio/ogg", "application/x-test" ]; let loadedTypes = xhr.responseText == "NOTHING" ? [] : xhr.responseText.split(","); - for (let loadedType of loadedTypes) { - isnot(allowedTypes.indexOf(loadedType), -1, "Check that " + loadedType + " was expected to load"); - } - // TODO: Media should be disabled after bug 759964. + is(loadedTypes.length, 4, "Check number of loaded content types: " + xhr.responseText) + todo_is(loadedTypes.length, 2, "Check number of loaded content types: " + xhr.responseText) + isnot(loadedTypes.indexOf("application/javascript"), -1, "Check JS was loaded"); isnot(loadedTypes.indexOf("text/html"), -1, "Check iframe was loaded"); todo_is(loadedTypes.indexOf("video/webm"), -1, "Check webm was not loaded"); todo_is(loadedTypes.indexOf("audio/ogg"), -1, "Check ogg was not loaded"); - - // Check that no plugin tags have a type other than TYPE_NULL (failed load) - // -- - // Checking if a channel was opened is not sufficient for plugin tags -- - // An object tag may still be allowed to load a sub-document, but not a - // plugin, so it will open a channel but then abort when it gets a - // plugin-type. - let doc = aSandbox._frame.contentDocument; - let nullType = Components.interfaces.nsIObjectLoadingContent.TYPE_NULL; - for (let tag of doc.querySelectorAll("embed, object, applet")) { - tag instanceof Components.interfaces.nsIObjectLoadingContent; - is(tag.displayedType, nullType, "Check that plugin did not load content"); - } } reset_server_state(); @@ -173,7 +157,7 @@ function check_disabled_content(aSandboxURL, aNothingShouldLoad = false) { let originalId = aSandbox.id; setTimeout(function() { - check_loaded_content(aSandbox, aNothingShouldLoad, function checkFinished() { + check_loaded_content(aNothingShouldLoad, function checkFinished() { info("reload the sandbox content"); aSandbox.reload(function sandboxReloadCB(aSandbox) { @@ -181,7 +165,7 @@ function check_disabled_content(aSandboxURL, aNothingShouldLoad = false) { is(aSandbox.id, originalId, "Sandbox ID should be the same after reload"); setTimeout(function() { - check_loaded_content(aSandbox, aNothingShouldLoad, function reloadCheckFinished() { + check_loaded_content(aNothingShouldLoad, function reloadCheckFinished() { free_and_check_sandbox(aSandbox); }); }, 5000);