CLOSED TREE
Backed out changeset 076d1d47d601 (bug 1070755)
Backed out changeset 43819af59ca5 (bug 998863)
Backed out changeset 5f587697ae63 (bug 998863)
Backed out changeset e2cf239e8572 (bug 998863)
Backed out changeset fe21b6b789ce (bug 998863)
Backed out changeset 404f59f86edc (bug 998863)
Backed out changeset 5dd57abaf2b0 (bug 998863)
Backed out changeset 6c1f006a03bc (bug 998863)
Backed out changeset 9e69875e3667 (bug 998863)
Backed out changeset c6b68f8f72ba (bug 998863)
Backed out changeset 713799a7afe4 (bug 998863)
Backed out changeset 4244d662787c (bug 998863)
Backed out changeset ba058cc7a1b2 (bug 998863)
Backed out changeset dabc69b0b09a (bug 998863)
Backed out changeset 18dad6d2e7cc (bug 998863)
1286 lines
34 KiB
C++
1286 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=2 et :
|
|
* 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/. */
|
|
|
|
#include "PluginScriptableObjectChild.h"
|
|
#include "PluginScriptableObjectUtils.h"
|
|
#include "mozilla/plugins/PluginTypes.h"
|
|
|
|
using namespace mozilla::plugins;
|
|
|
|
/**
|
|
* NPIdentifiers in the plugin process use a tagged representation. The low bit
|
|
* stores the tag. If it's zero, the identifier is a string, and the value is a
|
|
* pointer to a StoredIdentifier. If the tag bit is 1, then the rest of the
|
|
* NPIdentifier value is the integer itself. Like the JSAPI, we require that all
|
|
* integers stored in NPIdentifier be non-negative.
|
|
*
|
|
* String identifiers are stored in the sIdentifiers hashtable to ensure
|
|
* uniqueness. The lifetime of these identifiers is only as long as the incoming
|
|
* IPC call from the chrome process. If the plugin wants to retain an
|
|
* identifier, it needs to call NPN_GetStringIdentifier, which causes the
|
|
* mPermanent flag to be set on the identifier. When this flag is set, the
|
|
* identifier is saved until the plugin process exits.
|
|
*
|
|
* The StackIdentifier RAII class is used to manage ownership of
|
|
* identifiers. Any identifier obtained from this class should not be used
|
|
* outside its scope, except when the MakePermanent() method has been called on
|
|
* it.
|
|
*
|
|
* The lifetime of an NPIdentifier in the plugin process is totally divorced
|
|
* from the lifetime of an NPIdentifier in the chrome process (where an
|
|
* NPIdentifier is stored as a jsid). The JS GC in the chrome process is able to
|
|
* trace through the entire heap, unlike in the plugin process, so there is no
|
|
* reason to retain identifiers there.
|
|
*/
|
|
|
|
PluginScriptableObjectChild::IdentifierTable PluginScriptableObjectChild::sIdentifiers;
|
|
|
|
/* static */ PluginScriptableObjectChild::StoredIdentifier*
|
|
PluginScriptableObjectChild::HashIdentifier(const nsCString& aIdentifier)
|
|
{
|
|
StoredIdentifier* stored = sIdentifiers.Get(aIdentifier);
|
|
if (stored) {
|
|
return stored;
|
|
}
|
|
|
|
stored = new StoredIdentifier(aIdentifier);
|
|
sIdentifiers.Put(aIdentifier, stored);
|
|
return stored;
|
|
}
|
|
|
|
/* static */ void
|
|
PluginScriptableObjectChild::UnhashIdentifier(StoredIdentifier* aStored)
|
|
{
|
|
MOZ_ASSERT(sIdentifiers.Get(aStored->mIdentifier));
|
|
sIdentifiers.Remove(aStored->mIdentifier);
|
|
}
|
|
|
|
/* static */ void
|
|
PluginScriptableObjectChild::ClearIdentifiers()
|
|
{
|
|
sIdentifiers.Clear();
|
|
}
|
|
|
|
PluginScriptableObjectChild::StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier)
|
|
: mIdentifier(aIdentifier),
|
|
mStored(nullptr)
|
|
{
|
|
if (aIdentifier.type() == PluginIdentifier::TnsCString) {
|
|
mStored = PluginScriptableObjectChild::HashIdentifier(mIdentifier.get_nsCString());
|
|
}
|
|
}
|
|
|
|
PluginScriptableObjectChild::StackIdentifier::StackIdentifier(NPIdentifier aIdentifier)
|
|
: mStored(nullptr)
|
|
{
|
|
uintptr_t bits = reinterpret_cast<uintptr_t>(aIdentifier);
|
|
if (bits & 1) {
|
|
int32_t num = int32_t(bits >> 1);
|
|
mIdentifier = PluginIdentifier(num);
|
|
} else {
|
|
mStored = static_cast<StoredIdentifier*>(aIdentifier);
|
|
mIdentifier = mStored->mIdentifier;
|
|
}
|
|
}
|
|
|
|
PluginScriptableObjectChild::StackIdentifier::~StackIdentifier()
|
|
{
|
|
if (!mStored) {
|
|
return;
|
|
}
|
|
|
|
// Each StackIdentifier owns one reference to its StoredIdentifier. In
|
|
// addition, the sIdentifiers table owns a reference. If mPermanent is false
|
|
// and sIdentifiers has the last reference, then we want to remove the
|
|
// StoredIdentifier from the table (and destroy it).
|
|
StoredIdentifier *stored = mStored;
|
|
mStored = nullptr;
|
|
if (stored->mRefCnt == 1 && !stored->mPermanent) {
|
|
PluginScriptableObjectChild::UnhashIdentifier(stored);
|
|
}
|
|
}
|
|
|
|
NPIdentifier
|
|
PluginScriptableObjectChild::StackIdentifier::ToNPIdentifier() const
|
|
{
|
|
if (mStored) {
|
|
MOZ_ASSERT(mIdentifier.type() == PluginIdentifier::TnsCString);
|
|
MOZ_ASSERT((reinterpret_cast<uintptr_t>(mStored.get()) & 1) == 0);
|
|
return mStored;
|
|
}
|
|
|
|
int32_t num = mIdentifier.get_int32_t();
|
|
// The JS engine imposes this condition on int32s in jsids, so we assume it.
|
|
MOZ_ASSERT(num >= 0);
|
|
return reinterpret_cast<NPIdentifier>((num << 1) | 1);
|
|
}
|
|
|
|
static PluginIdentifier
|
|
FromNPIdentifier(NPIdentifier aIdentifier)
|
|
{
|
|
PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
|
|
return stack.GetIdentifier();
|
|
}
|
|
|
|
// static
|
|
NPObject*
|
|
PluginScriptableObjectChild::ScriptableAllocate(NPP aInstance,
|
|
NPClass* aClass)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aClass != GetClass()) {
|
|
NS_RUNTIMEABORT("Huh?! Wrong class!");
|
|
}
|
|
|
|
return new ChildNPObject();
|
|
}
|
|
|
|
// static
|
|
void
|
|
PluginScriptableObjectChild::ScriptableInvalidate(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
// This can happen more than once, and is just fine.
|
|
return;
|
|
}
|
|
|
|
object->invalidated = true;
|
|
}
|
|
|
|
// static
|
|
void
|
|
PluginScriptableObjectChild::ScriptableDeallocate(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
PluginScriptableObjectChild* actor = object->parent;
|
|
if (actor) {
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
actor->DropNPObject();
|
|
}
|
|
|
|
delete object;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableHasMethod(NPObject* aObject,
|
|
NPIdentifier aName)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
bool result;
|
|
actor->CallHasMethod(FromNPIdentifier(aName), &result);
|
|
|
|
return result;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableInvoke(NPObject* aObject,
|
|
NPIdentifier aName,
|
|
const NPVariant* aArgs,
|
|
uint32_t aArgCount,
|
|
NPVariant* aResult)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
|
|
if (!args.IsOk()) {
|
|
NS_ERROR("Failed to convert arguments!");
|
|
return false;
|
|
}
|
|
|
|
Variant remoteResult;
|
|
bool success;
|
|
actor->CallInvoke(FromNPIdentifier(aName), args,
|
|
&remoteResult, &success);
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
ConvertToVariant(remoteResult, *aResult);
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableInvokeDefault(NPObject* aObject,
|
|
const NPVariant* aArgs,
|
|
uint32_t aArgCount,
|
|
NPVariant* aResult)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
|
|
if (!args.IsOk()) {
|
|
NS_ERROR("Failed to convert arguments!");
|
|
return false;
|
|
}
|
|
|
|
Variant remoteResult;
|
|
bool success;
|
|
actor->CallInvokeDefault(args, &remoteResult, &success);
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
ConvertToVariant(remoteResult, *aResult);
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableHasProperty(NPObject* aObject,
|
|
NPIdentifier aName)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
bool result;
|
|
actor->CallHasProperty(FromNPIdentifier(aName), &result);
|
|
|
|
return result;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject,
|
|
NPIdentifier aName,
|
|
NPVariant* aResult)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
Variant result;
|
|
bool success;
|
|
actor->CallGetParentProperty(FromNPIdentifier(aName),
|
|
&result, &success);
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
ConvertToVariant(result, *aResult);
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableSetProperty(NPObject* aObject,
|
|
NPIdentifier aName,
|
|
const NPVariant* aValue)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
ProtectedVariant value(*aValue, actor->GetInstance());
|
|
if (!value.IsOk()) {
|
|
NS_WARNING("Failed to convert variant!");
|
|
return false;
|
|
}
|
|
|
|
bool success;
|
|
actor->CallSetProperty(FromNPIdentifier(aName), value,
|
|
&success);
|
|
|
|
return success;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableRemoveProperty(NPObject* aObject,
|
|
NPIdentifier aName)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
bool success;
|
|
actor->CallRemoveProperty(FromNPIdentifier(aName),
|
|
&success);
|
|
|
|
return success;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject,
|
|
NPIdentifier** aIdentifiers,
|
|
uint32_t* aCount)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
AutoInfallibleTArray<PluginIdentifier, 10> identifiers;
|
|
bool success;
|
|
actor->CallEnumerate(&identifiers, &success);
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
*aCount = identifiers.Length();
|
|
if (!*aCount) {
|
|
*aIdentifiers = nullptr;
|
|
return true;
|
|
}
|
|
|
|
*aIdentifiers = reinterpret_cast<NPIdentifier*>(
|
|
PluginModuleChild::sBrowserFuncs.memalloc(*aCount * sizeof(NPIdentifier)));
|
|
if (!*aIdentifiers) {
|
|
NS_ERROR("Out of memory!");
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t index = 0; index < *aCount; index++) {
|
|
StackIdentifier id(identifiers[index]);
|
|
// Make the id permanent in case the plugin retains it.
|
|
id.MakePermanent();
|
|
(*aIdentifiers)[index] = id.ToNPIdentifier();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
PluginScriptableObjectChild::ScriptableConstruct(NPObject* aObject,
|
|
const NPVariant* aArgs,
|
|
uint32_t aArgCount,
|
|
NPVariant* aResult)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (aObject->_class != GetClass()) {
|
|
NS_RUNTIMEABORT("Don't know what kind of object this is!");
|
|
}
|
|
|
|
ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
|
|
if (object->invalidated) {
|
|
NS_WARNING("Calling method on an invalidated object!");
|
|
return false;
|
|
}
|
|
|
|
ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
|
|
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
|
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
|
|
|
ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
|
|
if (!args.IsOk()) {
|
|
NS_ERROR("Failed to convert arguments!");
|
|
return false;
|
|
}
|
|
|
|
Variant remoteResult;
|
|
bool success;
|
|
actor->CallConstruct(args, &remoteResult, &success);
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
ConvertToVariant(remoteResult, *aResult);
|
|
return true;
|
|
}
|
|
|
|
const NPClass PluginScriptableObjectChild::sNPClass = {
|
|
NP_CLASS_STRUCT_VERSION,
|
|
PluginScriptableObjectChild::ScriptableAllocate,
|
|
PluginScriptableObjectChild::ScriptableDeallocate,
|
|
PluginScriptableObjectChild::ScriptableInvalidate,
|
|
PluginScriptableObjectChild::ScriptableHasMethod,
|
|
PluginScriptableObjectChild::ScriptableInvoke,
|
|
PluginScriptableObjectChild::ScriptableInvokeDefault,
|
|
PluginScriptableObjectChild::ScriptableHasProperty,
|
|
PluginScriptableObjectChild::ScriptableGetProperty,
|
|
PluginScriptableObjectChild::ScriptableSetProperty,
|
|
PluginScriptableObjectChild::ScriptableRemoveProperty,
|
|
PluginScriptableObjectChild::ScriptableEnumerate,
|
|
PluginScriptableObjectChild::ScriptableConstruct
|
|
};
|
|
|
|
PluginScriptableObjectChild::PluginScriptableObjectChild(
|
|
ScriptableObjectType aType)
|
|
: mInstance(nullptr),
|
|
mObject(nullptr),
|
|
mInvalidated(false),
|
|
mProtectCount(0),
|
|
mType(aType)
|
|
{
|
|
AssertPluginThread();
|
|
}
|
|
|
|
PluginScriptableObjectChild::~PluginScriptableObjectChild()
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mObject) {
|
|
UnregisterActor(mObject);
|
|
|
|
if (mObject->_class == GetClass()) {
|
|
NS_ASSERTION(mType == Proxy, "Wrong type!");
|
|
static_cast<ChildNPObject*>(mObject)->parent = nullptr;
|
|
}
|
|
else {
|
|
NS_ASSERTION(mType == LocalObject, "Wrong type!");
|
|
PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::InitializeProxy()
|
|
{
|
|
AssertPluginThread();
|
|
NS_ASSERTION(mType == Proxy, "Bad type!");
|
|
NS_ASSERTION(!mObject, "Calling Initialize more than once!");
|
|
NS_ASSERTION(!mInvalidated, "Already invalidated?!");
|
|
|
|
mInstance = static_cast<PluginInstanceChild*>(Manager());
|
|
NS_ASSERTION(mInstance, "Null manager?!");
|
|
|
|
NPObject* object = CreateProxyObject();
|
|
if (!object) {
|
|
NS_ERROR("Failed to create object!");
|
|
return false;
|
|
}
|
|
|
|
if (!RegisterActor(object)) {
|
|
NS_ERROR("RegisterActor failed");
|
|
return false;
|
|
}
|
|
|
|
mObject = object;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginScriptableObjectChild::InitializeLocal(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
NS_ASSERTION(!mObject, "Calling Initialize more than once!");
|
|
NS_ASSERTION(!mInvalidated, "Already invalidated?!");
|
|
|
|
mInstance = static_cast<PluginInstanceChild*>(Manager());
|
|
NS_ASSERTION(mInstance, "Null manager?!");
|
|
|
|
PluginModuleChild::sBrowserFuncs.retainobject(aObject);
|
|
|
|
NS_ASSERTION(!mProtectCount, "Should be zero!");
|
|
mProtectCount++;
|
|
|
|
if (!RegisterActor(aObject)) {
|
|
NS_ERROR("RegisterActor failed");
|
|
}
|
|
|
|
mObject = aObject;
|
|
}
|
|
|
|
NPObject*
|
|
PluginScriptableObjectChild::CreateProxyObject()
|
|
{
|
|
NS_ASSERTION(mInstance, "Must have an instance!");
|
|
NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
|
|
|
|
NPClass* proxyClass = const_cast<NPClass*>(GetClass());
|
|
NPObject* npobject =
|
|
PluginModuleChild::sBrowserFuncs.createobject(mInstance->GetNPP(),
|
|
proxyClass);
|
|
NS_ASSERTION(npobject, "Failed to create object?!");
|
|
NS_ASSERTION(npobject->_class == GetClass(), "Wrong kind of object!");
|
|
NS_ASSERTION(npobject->referenceCount == 1, "Some kind of live object!");
|
|
|
|
ChildNPObject* object = static_cast<ChildNPObject*>(npobject);
|
|
NS_ASSERTION(!object->invalidated, "Bad object!");
|
|
NS_ASSERTION(!object->parent, "Bad object!");
|
|
|
|
// We don't want to have the actor own this object but rather let the object
|
|
// own this actor. Set the reference count to 0 here so that when the object
|
|
// dies we will send the destructor message to the child.
|
|
object->referenceCount = 0;
|
|
NS_LOG_RELEASE(object, 0, "NPObject");
|
|
|
|
object->parent = const_cast<PluginScriptableObjectChild*>(this);
|
|
return object;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::ResurrectProxyObject()
|
|
{
|
|
NS_ASSERTION(mInstance, "Must have an instance already!");
|
|
NS_ASSERTION(!mObject, "Should not have an object already!");
|
|
NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
|
|
|
|
if (!InitializeProxy()) {
|
|
NS_ERROR("Initialize failed!");
|
|
return false;
|
|
}
|
|
|
|
SendProtect();
|
|
return true;
|
|
}
|
|
|
|
NPObject*
|
|
PluginScriptableObjectChild::GetObject(bool aCanResurrect)
|
|
{
|
|
if (!mObject && aCanResurrect && !ResurrectProxyObject()) {
|
|
NS_ERROR("Null object!");
|
|
return nullptr;
|
|
}
|
|
return mObject;
|
|
}
|
|
|
|
void
|
|
PluginScriptableObjectChild::Protect()
|
|
{
|
|
NS_ASSERTION(mObject, "No object!");
|
|
NS_ASSERTION(mProtectCount >= 0, "Negative retain count?!");
|
|
|
|
if (mType == LocalObject) {
|
|
++mProtectCount;
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginScriptableObjectChild::Unprotect()
|
|
{
|
|
NS_ASSERTION(mObject, "Bad state!");
|
|
NS_ASSERTION(mProtectCount >= 0, "Negative retain count?!");
|
|
|
|
if (mType == LocalObject) {
|
|
if (--mProtectCount == 0) {
|
|
PluginScriptableObjectChild::Send__delete__(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginScriptableObjectChild::DropNPObject()
|
|
{
|
|
NS_ASSERTION(mObject, "Invalidated object!");
|
|
NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!");
|
|
NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
|
|
|
|
// We think we're about to be deleted, but we could be racing with the other
|
|
// process.
|
|
UnregisterActor(mObject);
|
|
mObject = nullptr;
|
|
|
|
SendUnprotect();
|
|
}
|
|
|
|
void
|
|
PluginScriptableObjectChild::NPObjectDestroyed()
|
|
{
|
|
NS_ASSERTION(LocalObject == mType,
|
|
"ScriptableDeallocate should have handled this for proxies");
|
|
mInvalidated = true;
|
|
mObject = nullptr;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerInvalidate()
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
return true;
|
|
}
|
|
|
|
mInvalidated = true;
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (mObject->_class && mObject->_class->invalidate) {
|
|
mObject->_class->invalidate(mObject);
|
|
}
|
|
|
|
Unprotect();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerHasMethod(const PluginIdentifier& aId,
|
|
bool* aHasMethod)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
|
|
*aHasMethod = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->hasMethod)) {
|
|
*aHasMethod = false;
|
|
return true;
|
|
}
|
|
|
|
StackIdentifier id(aId);
|
|
*aHasMethod = mObject->_class->hasMethod(mObject, id.ToNPIdentifier());
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerInvoke(const PluginIdentifier& aId,
|
|
const InfallibleTArray<Variant>& aArgs,
|
|
Variant* aResult,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerInvoke with an invalidated object!");
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->invoke)) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
AutoFallibleTArray<NPVariant, 10> convertedArgs;
|
|
uint32_t argCount = aArgs.Length();
|
|
|
|
if (!convertedArgs.SetLength(argCount)) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
for (uint32_t index = 0; index < argCount; index++) {
|
|
ConvertToVariant(aArgs[index], convertedArgs[index]);
|
|
}
|
|
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
StackIdentifier id(aId);
|
|
bool success = mObject->_class->invoke(mObject, id.ToNPIdentifier(),
|
|
convertedArgs.Elements(), argCount,
|
|
&result);
|
|
|
|
for (uint32_t index = 0; index < argCount; index++) {
|
|
PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
|
|
}
|
|
|
|
if (!success) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
Variant convertedResult;
|
|
success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
|
|
false);
|
|
|
|
DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
|
|
|
|
if (!success) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
*aSuccess = true;
|
|
*aResult = convertedResult;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerInvokeDefault(const InfallibleTArray<Variant>& aArgs,
|
|
Variant* aResult,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerInvokeDefault with an invalidated object!");
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->invokeDefault)) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
AutoFallibleTArray<NPVariant, 10> convertedArgs;
|
|
uint32_t argCount = aArgs.Length();
|
|
|
|
if (!convertedArgs.SetLength(argCount)) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
for (uint32_t index = 0; index < argCount; index++) {
|
|
ConvertToVariant(aArgs[index], convertedArgs[index]);
|
|
}
|
|
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
bool success = mObject->_class->invokeDefault(mObject,
|
|
convertedArgs.Elements(),
|
|
argCount, &result);
|
|
|
|
for (uint32_t index = 0; index < argCount; index++) {
|
|
PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
|
|
}
|
|
|
|
if (!success) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
Variant convertedResult;
|
|
success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
|
|
false);
|
|
|
|
DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
|
|
|
|
if (!success) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
*aResult = convertedResult;
|
|
*aSuccess = true;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerHasProperty(const PluginIdentifier& aId,
|
|
bool* aHasProperty)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
|
|
*aHasProperty = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->hasProperty)) {
|
|
*aHasProperty = false;
|
|
return true;
|
|
}
|
|
|
|
StackIdentifier id(aId);
|
|
*aHasProperty = mObject->_class->hasProperty(mObject, id.ToNPIdentifier());
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerGetChildProperty(const PluginIdentifier& aId,
|
|
bool* aHasProperty,
|
|
bool* aHasMethod,
|
|
Variant* aResult,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
*aHasProperty = *aHasMethod = *aSuccess = false;
|
|
*aResult = void_t();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerGetProperty with an invalidated object!");
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->hasProperty &&
|
|
mObject->_class->hasMethod && mObject->_class->getProperty)) {
|
|
return true;
|
|
}
|
|
|
|
StackIdentifier stackID(aId);
|
|
NPIdentifier id = stackID.ToNPIdentifier();
|
|
|
|
*aHasProperty = mObject->_class->hasProperty(mObject, id);
|
|
*aHasMethod = mObject->_class->hasMethod(mObject, id);
|
|
|
|
if (*aHasProperty) {
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
|
|
if (!mObject->_class->getProperty(mObject, id, &result)) {
|
|
return true;
|
|
}
|
|
|
|
Variant converted;
|
|
if ((*aSuccess = ConvertToRemoteVariant(result, converted, GetInstance(),
|
|
false))) {
|
|
DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
|
|
*aResult = converted;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerSetProperty(const PluginIdentifier& aId,
|
|
const Variant& aValue,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->hasProperty &&
|
|
mObject->_class->setProperty)) {
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
StackIdentifier stackID(aId);
|
|
NPIdentifier id = stackID.ToNPIdentifier();
|
|
|
|
if (!mObject->_class->hasProperty(mObject, id)) {
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NPVariant converted;
|
|
ConvertToVariant(aValue, converted);
|
|
|
|
if ((*aSuccess = mObject->_class->setProperty(mObject, id, &converted))) {
|
|
PluginModuleChild::sBrowserFuncs.releasevariantvalue(&converted);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerRemoveProperty(const PluginIdentifier& aId,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->hasProperty &&
|
|
mObject->_class->removeProperty)) {
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
StackIdentifier stackID(aId);
|
|
NPIdentifier id = stackID.ToNPIdentifier();
|
|
*aSuccess = mObject->_class->hasProperty(mObject, id) ?
|
|
mObject->_class->removeProperty(mObject, id) :
|
|
true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->enumerate)) {
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NPIdentifier* ids;
|
|
uint32_t idCount;
|
|
if (!mObject->_class->enumerate(mObject, &ids, &idCount)) {
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
aProperties->SetCapacity(idCount);
|
|
|
|
for (uint32_t index = 0; index < idCount; index++) {
|
|
aProperties->AppendElement(FromNPIdentifier(ids[index]));
|
|
}
|
|
|
|
PluginModuleChild::sBrowserFuncs.memfree(ids);
|
|
*aSuccess = true;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::AnswerConstruct(const InfallibleTArray<Variant>& aArgs,
|
|
Variant* aResult,
|
|
bool* aSuccess)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mInvalidated) {
|
|
NS_WARNING("Calling AnswerConstruct with an invalidated object!");
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
if (!(mObject->_class && mObject->_class->construct)) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
AutoFallibleTArray<NPVariant, 10> convertedArgs;
|
|
uint32_t argCount = aArgs.Length();
|
|
|
|
if (!convertedArgs.SetLength(argCount)) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
for (uint32_t index = 0; index < argCount; index++) {
|
|
ConvertToVariant(aArgs[index], convertedArgs[index]);
|
|
}
|
|
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
bool success = mObject->_class->construct(mObject, convertedArgs.Elements(),
|
|
argCount, &result);
|
|
|
|
for (uint32_t index = 0; index < argCount; index++) {
|
|
PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
|
|
}
|
|
|
|
if (!success) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
Variant convertedResult;
|
|
success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
|
|
false);
|
|
|
|
DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
|
|
|
|
if (!success) {
|
|
*aResult = void_t();
|
|
*aSuccess = false;
|
|
return true;
|
|
}
|
|
|
|
*aResult = convertedResult;
|
|
*aSuccess = true;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::RecvProtect()
|
|
{
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
Protect();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::RecvUnprotect()
|
|
{
|
|
NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
|
|
NS_ASSERTION(mType == LocalObject, "Bad type!");
|
|
|
|
Unprotect();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginScriptableObjectChild::Evaluate(NPString* aScript,
|
|
NPVariant* aResult)
|
|
{
|
|
nsDependentCString script("");
|
|
if (aScript->UTF8Characters && aScript->UTF8Length) {
|
|
script.Rebind(aScript->UTF8Characters, aScript->UTF8Length);
|
|
}
|
|
|
|
bool success;
|
|
Variant result;
|
|
CallNPN_Evaluate(script, &result, &success);
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
ConvertToVariant(result, *aResult);
|
|
return true;
|
|
}
|
|
|
|
nsTHashtable<PluginScriptableObjectChild::NPObjectData>* PluginScriptableObjectChild::sObjectMap;
|
|
|
|
bool
|
|
PluginScriptableObjectChild::RegisterActor(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
MOZ_ASSERT(aObject, "Null pointer!");
|
|
|
|
NPObjectData* d = sObjectMap->GetEntry(aObject);
|
|
if (!d) {
|
|
NS_ERROR("NPObject not in object table");
|
|
return false;
|
|
}
|
|
|
|
d->actor = this;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginScriptableObjectChild::UnregisterActor(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
MOZ_ASSERT(aObject, "Null pointer!");
|
|
|
|
NPObjectData* d = sObjectMap->GetEntry(aObject);
|
|
MOZ_ASSERT(d, "NPObject not in object table");
|
|
if (d) {
|
|
d->actor = nullptr;
|
|
}
|
|
}
|
|
|
|
/* static */ PluginScriptableObjectChild*
|
|
PluginScriptableObjectChild::GetActorForNPObject(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
MOZ_ASSERT(aObject, "Null pointer!");
|
|
|
|
NPObjectData* d = sObjectMap->GetEntry(aObject);
|
|
if (!d) {
|
|
NS_ERROR("Plugin using object not created with NPN_CreateObject?");
|
|
return nullptr;
|
|
}
|
|
|
|
return d->actor;
|
|
}
|
|
|
|
/* static */ void
|
|
PluginScriptableObjectChild::RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (!sObjectMap) {
|
|
sObjectMap = new nsTHashtable<PluginScriptableObjectChild::NPObjectData>();
|
|
}
|
|
|
|
NPObjectData* d = sObjectMap->PutEntry(aObject);
|
|
MOZ_ASSERT(!d->instance, "New NPObject already mapped?");
|
|
d->instance = aInstance;
|
|
}
|
|
|
|
/* static */ void
|
|
PluginScriptableObjectChild::UnregisterObject(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
sObjectMap->RemoveEntry(aObject);
|
|
|
|
if (!sObjectMap->Count()) {
|
|
delete sObjectMap;
|
|
sObjectMap = nullptr;
|
|
}
|
|
}
|
|
|
|
/* static */ PluginInstanceChild*
|
|
PluginScriptableObjectChild::GetInstanceForNPObject(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
NPObjectData* d = sObjectMap->GetEntry(aObject);
|
|
if (!d) {
|
|
return nullptr;
|
|
}
|
|
return d->instance;
|
|
}
|
|
|
|
/* static */ PLDHashOperator
|
|
PluginScriptableObjectChild::CollectForInstance(NPObjectData* d, void* userArg)
|
|
{
|
|
PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(userArg);
|
|
if (d->instance == instance) {
|
|
NPObject* o = d->GetKey();
|
|
instance->mDeletingHash->PutEntry(o);
|
|
}
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
/* static */ void
|
|
PluginScriptableObjectChild::NotifyOfInstanceShutdown(PluginInstanceChild* aInstance)
|
|
{
|
|
AssertPluginThread();
|
|
sObjectMap->EnumerateEntries(CollectForInstance, aInstance);
|
|
}
|