diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 0c0892a2cad..a2e08e8be21 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -111,6 +111,7 @@ class imgIRequest; class nsISHEntry; class nsDOMNavigationTiming; class nsWindowSizes; +class nsIObjectLoadingContent; namespace mozilla { namespace css { @@ -1570,6 +1571,10 @@ public: // state is unlocked/false. virtual nsresult SetImageLockingState(bool aLocked) = 0; + virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin) = 0; + virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin) = 0; + virtual void GetPlugins(nsTArray& aPlugins) = 0; + virtual nsresult GetStateObject(nsIVariant** aResult) = 0; virtual nsDOMNavigationTiming* GetNavigationTiming() const = 0; diff --git a/content/base/public/nsIObjectLoadingContent.idl b/content/base/public/nsIObjectLoadingContent.idl index bf688caa76f..1b0a1a7f470 100644 --- a/content/base/public/nsIObjectLoadingContent.idl +++ b/content/base/public/nsIObjectLoadingContent.idl @@ -52,7 +52,7 @@ interface nsIURI; /** * This interface represents a content node that loads objects. */ -[scriptable, uuid(3FF07AB3-5BAC-4D98-9549-5BD15CCEBCD3)] +[scriptable, uuid(fd56fda8-d3c3-4368-8cf3-67dbc992aec9)] interface nsIObjectLoadingContent : nsISupports { const unsigned long TYPE_LOADING = 0; @@ -125,6 +125,12 @@ interface nsIObjectLoadingContent : nsISupports */ void playPlugin(); + /** + * This attribute will return true if the plugin has been activated + * and false if the plugin is still in the click-to-play state. + */ + readonly attribute boolean activated; + [noscript] void stopPluginInstance(); [noscript] void syncStartPluginInstance(); diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 65c48d1d882..bbf720b491c 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1674,6 +1674,8 @@ nsDocument::~nsDocument() // unlocked state, and then clear the table. SetImageLockingState(false); mImageTracker.Clear(); + + mPlugins.Clear(); } NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) @@ -2024,7 +2026,8 @@ nsDocument::Init() mScriptLoader = new nsScriptLoader(this); NS_ENSURE_TRUE(mScriptLoader, NS_ERROR_OUT_OF_MEMORY); - if (!mImageTracker.Init()) { + if (!mImageTracker.Init() || + !mPlugins.Init()) { return NS_ERROR_OUT_OF_MEMORY; } @@ -8362,6 +8365,51 @@ nsDocument::RemoveImage(imgIRequest* aImage) return rv; } +nsresult +nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin) +{ + MOZ_ASSERT(aPlugin); + if (!mPlugins.PutEntry(aPlugin)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +void +nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin) +{ + MOZ_ASSERT(aPlugin); + mPlugins.RemoveEntry(aPlugin); +} + +static bool +AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg) +{ + nsTArray* plugins = + reinterpret_cast< nsTArray* >(userArg); + MOZ_ASSERT(plugins); + aDocument->GetPlugins(*plugins); + return true; +} + +static PLDHashOperator +AllPluginEnum(nsPtrHashKey* aPlugin, void* userArg) +{ + nsTArray* allPlugins = + reinterpret_cast< nsTArray* >(userArg); + MOZ_ASSERT(allPlugins); + allPlugins->AppendElement(aPlugin->GetKey()); + return PL_DHASH_NEXT; +} + +void +nsDocument::GetPlugins(nsTArray& aPlugins) +{ + aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count()); + mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins); + EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins); +} + PLDHashOperator LockEnumerator(imgIRequest* aKey, PRUint32 aData, void* userArg) diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 28af366c485..f04e2b32981 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -935,6 +935,16 @@ public: virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage); virtual NS_HIDDEN_(nsresult) SetImageLockingState(bool aLocked); + // AddPlugin adds a plugin-related element to mPlugins when the element is + // added to the tree. + virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin); + // RemovePlugin removes a plugin-related element to mPlugins when the + // element is removed from the tree. + virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin); + // GetPlugins returns the plugin-related elements from + // the frame and any subframes. + virtual void GetPlugins(nsTArray& aPlugins); + virtual nsresult GetStateObject(nsIVariant** aResult); virtual nsDOMNavigationTiming* GetNavigationTiming() const; @@ -1304,6 +1314,9 @@ private: // Tracking for images in the document. nsDataHashtable< nsPtrHashKey, PRUint32> mImageTracker; + // Tracking for plugins in the document. + nsTHashtable< nsPtrHashKey > mPlugins; + VisibilityState mVisibilityState; #ifdef DEBUG diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index b10c389b6dd..f81f91d3637 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -115,6 +115,18 @@ static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc"); #include "mozilla/Preferences.h" +static bool gClickToPlayPlugins = false; + +static void +InitPrefCache() +{ + static bool initializedPrefCache = false; + if (!initializedPrefCache) { + mozilla::Preferences::AddBoolVarCache(&gClickToPlayPlugins, "plugins.click_to_play"); + } + initializedPrefCache = true; +} + class nsAsyncInstantiateEvent : public nsRunnable { public: nsObjectLoadingContent *mContent; @@ -556,6 +568,26 @@ bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString& return false; } +nsresult +nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* /*aParent*/, + nsIContent* /*aBindingParent*/, + bool /*aCompileEventHandlers*/) +{ + if (aDocument) { + return aDocument->AddPlugin(this); + } + return NS_OK; +} + +void +nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/) +{ + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); + MOZ_ASSERT(thisContent); + nsIDocument* ownerDoc = thisContent->OwnerDoc(); + ownerDoc->RemovePlugin(this); +} + nsObjectLoadingContent::nsObjectLoadingContent() : mPendingInstantiateEvent(nsnull) , mChannel(nsnull) @@ -564,11 +596,14 @@ nsObjectLoadingContent::nsObjectLoadingContent() , mUserDisabled(false) , mSuppressed(false) , mNetworkCreated(true) - // If plugins.click_to_play is false, plugins should always play - , mShouldPlay(!mozilla::Preferences::GetBool("plugins.click_to_play", false)) , mSrcStreamLoading(false) , mFallbackReason(ePluginOtherState) { + InitPrefCache(); + // If plugins.click_to_play is false, plugins should always play + mShouldPlay = !gClickToPlayPlugins; + // If plugins.click_to_play is true, track the activated state of plugins. + mActivated = !gClickToPlayPlugins; } nsObjectLoadingContent::~nsObjectLoadingContent() @@ -2216,5 +2251,13 @@ nsObjectLoadingContent::PlayPlugin() return NS_OK; mShouldPlay = true; + mActivated = true; return LoadObject(mURI, true, mContentType, true); } + +NS_IMETHODIMP +nsObjectLoadingContent::GetActivated(bool* aActivated) +{ + *aActivated = mActivated; + return NS_OK; +} diff --git a/content/base/src/nsObjectLoadingContent.h b/content/base/src/nsObjectLoadingContent.h index 720e9ca8bb9..8e24376f386 100644 --- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -244,6 +244,12 @@ class nsObjectLoadingContent : public nsImageLoadingContent static void DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, bool aDelayedStop); + nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandler); + void UnbindFromTree(bool aDeep = true, + bool aNullParent = true); + private: void NotifyContentObjectWrapper(); @@ -399,6 +405,10 @@ class nsObjectLoadingContent : public nsImageLoadingContent // This is used for click-to-play plugins. bool mShouldPlay : 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; + // 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 diff --git a/content/html/content/src/nsHTMLObjectElement.cpp b/content/html/content/src/nsHTMLObjectElement.cpp index a45e4de3035..b9d8a79bfba 100644 --- a/content/html/content/src/nsHTMLObjectElement.cpp +++ b/content/html/content/src/nsHTMLObjectElement.cpp @@ -265,6 +265,11 @@ nsHTMLObjectElement::BindToTree(nsIDocument *aDocument, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); + rv = nsObjectLoadingContent::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + // If we already have all the children, start the load. if (mIsDoneAddingChildren) { void (nsHTMLObjectElement::*start)() = &nsHTMLObjectElement::StartObjectLoad; @@ -279,6 +284,7 @@ nsHTMLObjectElement::UnbindFromTree(bool aDeep, bool aNullParent) { RemovedFromDocument(); + nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent); } diff --git a/content/html/content/src/nsHTMLSharedObjectElement.cpp b/content/html/content/src/nsHTMLSharedObjectElement.cpp index 177518aef2b..975f78211aa 100644 --- a/content/html/content/src/nsHTMLSharedObjectElement.cpp +++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp @@ -283,6 +283,11 @@ nsHTMLSharedObjectElement::BindToTree(nsIDocument *aDocument, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); + rv = nsObjectLoadingContent::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + // If we already have all the children, start the load. if (mIsDoneAddingChildren) { void (nsHTMLSharedObjectElement::*start)() = @@ -298,6 +303,7 @@ nsHTMLSharedObjectElement::UnbindFromTree(bool aDeep, bool aNullParent) { RemovedFromDocument(); + nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 6d81c651354..93519679afd 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -51,6 +51,7 @@ #include "nsRefreshDriver.h" #include "nsDOMTouchEvent.h" #include "nsIDOMTouchEvent.h" +#include "nsObjectLoadingContent.h" #include "nsIScrollableFrame.h" @@ -76,6 +77,7 @@ #include "nsCSSProps.h" #include "nsDOMFile.h" #include "BasicLayers.h" +#include "nsTArrayHelpers.h" #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2) #include @@ -2230,3 +2232,26 @@ nsDOMWindowUtils::GetPaintingSuppressed(bool *aPaintingSuppressed) return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::GetPlugins(JSContext* cx, jsval* aPlugins) +{ + if (!IsUniversalXPConnectCapable()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsIDOMDocument* ddoc = mWindow->GetExtantDocument(); + + nsresult rv; + nsCOMPtr doc = do_QueryInterface(ddoc, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsTArray plugins; + doc->GetPlugins(plugins); + + JSObject* jsPlugins = nsnull; + rv = nsTArrayToJSArray(cx, plugins, &jsPlugins); + NS_ENSURE_SUCCESS(rv, rv); + + *aPlugins = OBJECT_TO_JSVAL(jsPlugins); + return NS_OK; +} diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index b90c2b57e8b..a86f14081ae 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -70,7 +70,7 @@ interface nsIDOMFile; interface nsIFile; interface nsIDOMTouch; -[scriptable, uuid(43feb172-30e1-4ff1-b021-004f973da516)] +[scriptable, uuid(c7f303a1-4f7b-4d38-a192-c3f0e25dadb1)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1099,4 +1099,15 @@ interface nsIDOMWindowUtils : nsISupports { * otherwise. */ readonly attribute boolean paintingSuppressed; + + /** + * Returns an array of plugins on the page for opt-in activation. + * + * Cannot be accessed from unprivileged context (not content-accessible). + * Will throw a DOM security error if called without UniversalXPConnect + * privileges. + * + */ + [implicit_jscontext] + readonly attribute jsval plugins; }; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b7f09f38c48..efc784a06fd 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4090,7 +4090,7 @@ JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) { AssertNoGC(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, obj); + assertSameCompartment(cx, obj, *vp); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); return obj->setElement(cx, index, vp, false); } diff --git a/js/xpconnect/public/Makefile.in b/js/xpconnect/public/Makefile.in index ab1afea4b7b..ded8d99b62c 100644 --- a/js/xpconnect/public/Makefile.in +++ b/js/xpconnect/public/Makefile.in @@ -49,6 +49,7 @@ EXPORTS = \ nsAXPCNativeCallContext.h \ xpc_map_end.h \ nsAutoJSValHolder.h \ + nsTArrayHelpers.h \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/js/xpconnect/public/nsTArrayHelpers.h b/js/xpconnect/public/nsTArrayHelpers.h new file mode 100644 index 00000000000..1a5be9ece7e --- /dev/null +++ b/js/xpconnect/public/nsTArrayHelpers.h @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NSTARRAYHELPERS_H__ +#define __NSTARRAYHELPERS_H__ + +template +inline nsresult +nsTArrayToJSArray(JSContext* aCx, const nsTArray& aSourceArray, + JSObject** aResultArray) +{ + MOZ_ASSERT(aCx); + JSAutoRequest ar(aCx); + + JSObject* arrayObj = JS_NewArrayObject(aCx, aSourceArray.Length(), nsnull); + if (!arrayObj) { + NS_WARNING("JS_NewArrayObject failed!"); + return NS_ERROR_OUT_OF_MEMORY; + } + + JSObject* global = JS_GetGlobalForScopeChain(aCx); + MOZ_ASSERT(global); + + for (PRUint32 index = 0; index < aSourceArray.Length(); index++) { + nsCOMPtr obj; + nsresult rv = CallQueryInterface(aSourceArray[index], getter_AddRefs(obj)); + NS_ENSURE_SUCCESS(rv, rv); + + jsval wrappedVal; + rv = nsContentUtils::WrapNative(aCx, global, obj, &wrappedVal, nsnull, true); + NS_ENSURE_SUCCESS(rv, rv); + + if (!JS_SetElement(aCx, arrayObj, index, &wrappedVal)) { + NS_WARNING("JS_SetElement failed!"); + return NS_ERROR_FAILURE; + } + } + + if (!JS_FreezeObject(aCx, arrayObj)) { + NS_WARNING("JS_FreezeObject failed!"); + return NS_ERROR_FAILURE; + } + + *aResultArray = arrayObj; + return NS_OK; +} + +#endif /* __NSTARRAYHELPERS_H__ */