Bug 730318 - Implement a way for chrome js to enumerate the plugin objects on a page for activation. r=khuey

This commit is contained in:
Jared Wein 2012-03-22 13:53:59 -07:00
parent 4750f67a6e
commit 589f9135f9
13 changed files with 228 additions and 6 deletions

View File

@ -111,6 +111,7 @@ class imgIRequest;
class nsISHEntry;
class nsDOMNavigationTiming;
class nsWindowSizes;
class nsIObjectLoadingContent;
namespace mozilla {
namespace css {
@ -1566,6 +1567,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<nsIObjectLoadingContent*>& aPlugins) = 0;
virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
virtual nsDOMNavigationTiming* GetNavigationTiming() const = 0;

View File

@ -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();

View File

@ -1673,6 +1673,8 @@ nsDocument::~nsDocument()
// unlocked state, and then clear the table.
SetImageLockingState(false);
mImageTracker.Clear();
mPlugins.Clear();
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
@ -2023,7 +2025,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;
}
@ -8354,6 +8357,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<nsIObjectLoadingContent*>* plugins =
reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
MOZ_ASSERT(plugins);
aDocument->GetPlugins(*plugins);
return true;
}
static PLDHashOperator
AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg)
{
nsTArray<nsIObjectLoadingContent*>* allPlugins =
reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
MOZ_ASSERT(allPlugins);
allPlugins->AppendElement(aPlugin->GetKey());
return PL_DHASH_NEXT;
}
void
nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
{
aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins);
EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
}
PLDHashOperator LockEnumerator(imgIRequest* aKey,
PRUint32 aData,
void* userArg)

View File

@ -934,6 +934,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<nsIObjectLoadingContent*>& aPlugins);
virtual nsresult GetStateObject(nsIVariant** aResult);
virtual nsDOMNavigationTiming* GetNavigationTiming() const;
@ -1298,6 +1308,9 @@ private:
// Tracking for images in the document.
nsDataHashtable< nsPtrHashKey<imgIRequest>, PRUint32> mImageTracker;
// Tracking for plugins in the document.
nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins;
VisibilityState mVisibilityState;
#ifdef DEBUG

View File

@ -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;
@ -546,6 +558,25 @@ 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<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
MOZ_ASSERT(thisContent);
nsIDocument* ownerDoc = thisContent->OwnerDoc();
ownerDoc->RemovePlugin(this);
}
nsObjectLoadingContent::nsObjectLoadingContent()
: mPendingInstantiateEvent(nsnull)
, mChannel(nsnull)
@ -554,11 +585,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()
@ -2205,5 +2239,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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 <gdk/gdk.h>
@ -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<nsIDocument> doc = do_QueryInterface(ddoc, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<nsIObjectLoadingContent*> 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;
}

View File

@ -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;
};

View File

@ -4104,7 +4104,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);
}

View File

@ -49,6 +49,7 @@ EXPORTS = \
nsAXPCNativeCallContext.h \
xpc_map_end.h \
nsAutoJSValHolder.h \
nsTArrayHelpers.h \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -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 <class T>
inline nsresult
nsTArrayToJSArray(JSContext* aCx, const nsTArray<T>& 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<nsISupports> 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__ */