summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc/PluginAsyncSurrogate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/ipc/PluginAsyncSurrogate.cpp')
-rw-r--r--dom/plugins/ipc/PluginAsyncSurrogate.cpp998
1 files changed, 998 insertions, 0 deletions
diff --git a/dom/plugins/ipc/PluginAsyncSurrogate.cpp b/dom/plugins/ipc/PluginAsyncSurrogate.cpp
new file mode 100644
index 000000000..da07116cc
--- /dev/null
+++ b/dom/plugins/ipc/PluginAsyncSurrogate.cpp
@@ -0,0 +1,998 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "PluginAsyncSurrogate.h"
+
+#include "base/message_loop.h"
+#include "base/message_pump_default.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/plugins/PluginModuleParent.h"
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+#include "mozilla/Telemetry.h"
+#include "nsJSNPRuntime.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "npruntime.h"
+#include "nsThreadUtils.h"
+#include "PluginMessageUtils.h"
+
+namespace mozilla {
+namespace plugins {
+
+AsyncNPObject::AsyncNPObject(PluginAsyncSurrogate* aSurrogate)
+ : NPObject()
+ , mSurrogate(aSurrogate)
+ , mRealObject(nullptr)
+{
+}
+
+AsyncNPObject::~AsyncNPObject()
+{
+ if (mRealObject) {
+ --mRealObject->asyncWrapperCount;
+ parent::_releaseobject(mRealObject);
+ mRealObject = nullptr;
+ }
+}
+
+NPObject*
+AsyncNPObject::GetRealObject()
+{
+ if (mRealObject) {
+ return mRealObject;
+ }
+ PluginInstanceParent* instance = PluginInstanceParent::Cast(mSurrogate->GetNPP());
+ if (!instance) {
+ return nullptr;
+ }
+ NPObject* realObject = nullptr;
+ NPError err = instance->NPP_GetValue(NPPVpluginScriptableNPObject,
+ &realObject);
+ if (err != NPERR_NO_ERROR) {
+ return nullptr;
+ }
+ if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ parent::_releaseobject(realObject);
+ return nullptr;
+ }
+ mRealObject = static_cast<ParentNPObject*>(realObject);
+ ++mRealObject->asyncWrapperCount;
+ return mRealObject;
+}
+
+class MOZ_STACK_CLASS RecursionGuard
+{
+public:
+ RecursionGuard()
+ : mIsRecursive(sHasEntered)
+ {
+ if (!mIsRecursive) {
+ sHasEntered = true;
+ }
+ }
+
+ ~RecursionGuard()
+ {
+ if (!mIsRecursive) {
+ sHasEntered = false;
+ }
+ }
+
+ inline bool
+ IsRecursive()
+ {
+ return mIsRecursive;
+ }
+
+private:
+ bool mIsRecursive;
+ static bool sHasEntered;
+};
+
+bool RecursionGuard::sHasEntered = false;
+
+PluginAsyncSurrogate::PluginAsyncSurrogate(PluginModuleParent* aParent)
+ : mParent(aParent)
+ , mMode(0)
+ , mWindow(nullptr)
+ , mAcceptCalls(false)
+ , mInstantiated(false)
+ , mAsyncSetWindow(false)
+ , mInitCancelled(false)
+ , mDestroyPending(false)
+ , mAsyncCallsInFlight(0)
+{
+ MOZ_ASSERT(aParent);
+}
+
+PluginAsyncSurrogate::~PluginAsyncSurrogate()
+{
+}
+
+bool
+PluginAsyncSurrogate::Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode,
+ int16_t aArgc, char* aArgn[], char* aArgv[])
+{
+ mMimeType = aPluginType;
+ nsNPAPIPluginInstance* instance =
+ static_cast<nsNPAPIPluginInstance*>(aInstance->ndata);
+ MOZ_ASSERT(instance);
+ mInstance = instance;
+ mMode = aMode;
+ for (int i = 0; i < aArgc; ++i) {
+ mNames.AppendElement(NullableString(aArgn[i]));
+ mValues.AppendElement(NullableString(aArgv[i]));
+ }
+ return true;
+}
+
+/* static */ bool
+PluginAsyncSurrogate::Create(PluginModuleParent* aParent, NPMIMEType aPluginType,
+ NPP aInstance, uint16_t aMode, int16_t aArgc,
+ char* aArgn[], char* aArgv[])
+{
+ RefPtr<PluginAsyncSurrogate> surrogate(new PluginAsyncSurrogate(aParent));
+ if (!surrogate->Init(aPluginType, aInstance, aMode, aArgc, aArgn, aArgv)) {
+ return false;
+ }
+ PluginAsyncSurrogate* rawSurrogate = nullptr;
+ surrogate.forget(&rawSurrogate);
+ aInstance->pdata = static_cast<PluginDataResolver*>(rawSurrogate);
+ return true;
+}
+
+/* static */ PluginAsyncSurrogate*
+PluginAsyncSurrogate::Cast(NPP aInstance)
+{
+ MOZ_ASSERT(aInstance);
+ PluginDataResolver* resolver =
+ reinterpret_cast<PluginDataResolver*>(aInstance->pdata);
+ if (!resolver) {
+ return nullptr;
+ }
+ return resolver->GetAsyncSurrogate();
+}
+
+nsresult
+PluginAsyncSurrogate::NPP_New(NPError* aError)
+{
+ if (!mInstance) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv = mParent->NPP_NewInternal(mMimeType.BeginWriting(), GetNPP(),
+ mMode, mNames, mValues, nullptr,
+ aError);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+void
+PluginAsyncSurrogate::NP_GetEntryPoints(NPPluginFuncs* aFuncs)
+{
+ aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ aFuncs->destroy = &NPP_Destroy;
+ aFuncs->getvalue = &NPP_GetValue;
+ aFuncs->setvalue = &NPP_SetValue;
+ aFuncs->newstream = &NPP_NewStream;
+ aFuncs->setwindow = &NPP_SetWindow;
+ aFuncs->writeready = &NPP_WriteReady;
+ aFuncs->event = &NPP_HandleEvent;
+ aFuncs->destroystream = &NPP_DestroyStream;
+ // We need to set these so that content code doesn't make assumptions
+ // about these operations not being supported
+ aFuncs->write = &PluginModuleParent::NPP_Write;
+ aFuncs->asfile = &PluginModuleParent::NPP_StreamAsFile;
+}
+
+/* static */ void
+PluginAsyncSurrogate::NotifyDestroyPending(NPP aInstance)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ if (!surrogate) {
+ return;
+ }
+ surrogate->NotifyDestroyPending();
+}
+
+NPP
+PluginAsyncSurrogate::GetNPP()
+{
+ MOZ_ASSERT(mInstance);
+ NPP npp;
+ DebugOnly<nsresult> rv = mInstance->GetNPP(&npp);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return npp;
+}
+
+void
+PluginAsyncSurrogate::NotifyDestroyPending()
+{
+ mDestroyPending = true;
+ nsJSNPRuntime::OnPluginDestroyPending(GetNPP());
+}
+
+NPError
+PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave)
+{
+ NotifyDestroyPending();
+ if (!WaitForInit()) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return PluginModuleParent::NPP_Destroy(GetNPP(), aSave);
+}
+
+NPError
+PluginAsyncSurrogate::NPP_GetValue(NPPVariable aVariable, void* aRetval)
+{
+ if (aVariable != NPPVpluginScriptableNPObject) {
+ if (!WaitForInit()) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ PluginInstanceParent* instance = PluginInstanceParent::Cast(GetNPP());
+ MOZ_ASSERT(instance);
+ return instance->NPP_GetValue(aVariable, aRetval);
+ }
+
+ NPObject* npobject = parent::_createobject(GetNPP(),
+ const_cast<NPClass*>(GetClass()));
+ MOZ_ASSERT(npobject);
+ MOZ_ASSERT(npobject->_class == GetClass());
+ MOZ_ASSERT(npobject->referenceCount == 1);
+ *(NPObject**)aRetval = npobject;
+ return npobject ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_SetValue(NPNVariable aVariable, void* aValue)
+{
+ if (!WaitForInit()) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return PluginModuleParent::NPP_SetValue(GetNPP(), aVariable, aValue);
+}
+
+NPError
+PluginAsyncSurrogate::NPP_NewStream(NPMIMEType aType, NPStream* aStream,
+ NPBool aSeekable, uint16_t* aStype)
+{
+ mPendingNewStreamCalls.AppendElement(PendingNewStreamCall(aType, aStream,
+ aSeekable));
+ if (aStype) {
+ *aStype = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_SetWindow(NPWindow* aWindow)
+{
+ mWindow = aWindow;
+ mAsyncSetWindow = false;
+ return NPERR_NO_ERROR;
+}
+
+nsresult
+PluginAsyncSurrogate::AsyncSetWindow(NPWindow* aWindow)
+{
+ mWindow = aWindow;
+ mAsyncSetWindow = true;
+ return NS_OK;
+}
+
+void
+PluginAsyncSurrogate::NPP_Print(NPPrint* aPrintInfo)
+{
+ // Do nothing, we've got nothing to print right now
+}
+
+int16_t
+PluginAsyncSurrogate::NPP_HandleEvent(void* event)
+{
+ // Drop the event -- the plugin isn't around to handle it
+ return false;
+}
+
+int32_t
+PluginAsyncSurrogate::NPP_WriteReady(NPStream* aStream)
+{
+ // We'll tell the browser to retry in a bit. Eventually NPP_WriteReady
+ // will resolve to the plugin's NPP_WriteReady and this should all just work.
+ return 0;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_DestroyStream(NPStream* aStream, NPReason aReason)
+{
+ for (uint32_t idx = 0, len = mPendingNewStreamCalls.Length(); idx < len; ++idx) {
+ PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[idx];
+ if (curPendingCall.mStream == aStream) {
+ mPendingNewStreamCalls.RemoveElementAt(idx);
+ break;
+ }
+ }
+ return NPERR_NO_ERROR;
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_Destroy(NPP aInstance, NPSavedData** aSave)
+{
+ PluginAsyncSurrogate* rawSurrogate = Cast(aInstance);
+ MOZ_ASSERT(rawSurrogate);
+ PluginModuleParent* module = rawSurrogate->GetParent();
+ if (module && !module->IsInitialized()) {
+ // Take ownership of pdata's surrogate since we're going to release it
+ RefPtr<PluginAsyncSurrogate> surrogate(dont_AddRef(rawSurrogate));
+ aInstance->pdata = nullptr;
+ // We haven't actually called NPP_New yet, so we should remove the
+ // surrogate for this instance.
+ bool removeOk = module->RemovePendingSurrogate(surrogate);
+ MOZ_ASSERT(removeOk);
+ if (!removeOk) {
+ return NPERR_GENERIC_ERROR;
+ }
+ surrogate->mInitCancelled = true;
+ return NPERR_NO_ERROR;
+ }
+ return rawSurrogate->NPP_Destroy(aSave);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_GetValue(NPP aInstance, NPPVariable aVariable,
+ void* aRetval)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_GetValue(aVariable, aRetval);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_SetValue(NPP aInstance, NPNVariable aVariable,
+ void* aValue)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_SetValue(aVariable, aValue);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_NewStream(NPP aInstance, NPMIMEType aType,
+ NPStream* aStream, NPBool aSeekable,
+ uint16_t* aStype)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_NewStream(aType, aStream, aSeekable, aStype);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_SetWindow(NPP aInstance, NPWindow* aWindow)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_SetWindow(aWindow);
+}
+
+/* static */ void
+PluginAsyncSurrogate::NPP_Print(NPP aInstance, NPPrint* aPrintInfo)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ surrogate->NPP_Print(aPrintInfo);
+}
+
+/* static */ int16_t
+PluginAsyncSurrogate::NPP_HandleEvent(NPP aInstance, void* aEvent)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_HandleEvent(aEvent);
+}
+
+/* static */ int32_t
+PluginAsyncSurrogate::NPP_WriteReady(NPP aInstance, NPStream* aStream)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_WriteReady(aStream);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_DestroyStream(NPP aInstance,
+ NPStream* aStream,
+ NPReason aReason)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_DestroyStream(aStream, aReason);
+}
+
+PluginAsyncSurrogate::PendingNewStreamCall::PendingNewStreamCall(
+ NPMIMEType aType, NPStream* aStream, NPBool aSeekable)
+ : mType(NullableString(aType))
+ , mStream(aStream)
+ , mSeekable(aSeekable)
+{
+}
+
+/* static */ nsNPAPIPluginStreamListener*
+PluginAsyncSurrogate::GetStreamListener(NPStream* aStream)
+{
+ nsNPAPIStreamWrapper* wrapper =
+ reinterpret_cast<nsNPAPIStreamWrapper*>(aStream->ndata);
+ if (!wrapper) {
+ return nullptr;
+ }
+ return wrapper->GetStreamListener();
+}
+
+void
+PluginAsyncSurrogate::DestroyAsyncStream(NPStream* aStream)
+{
+ MOZ_ASSERT(aStream);
+ nsNPAPIPluginStreamListener* streamListener = GetStreamListener(aStream);
+ MOZ_ASSERT(streamListener);
+ // streamListener was suspended during async init. We must resume the stream
+ // request prior to calling _destroystream for cleanup to work correctly.
+ streamListener->ResumeRequest();
+ if (!mInstance) {
+ return;
+ }
+ parent::_destroystream(GetNPP(), aStream, NPRES_DONE);
+}
+
+/* static */ bool
+PluginAsyncSurrogate::SetStreamType(NPStream* aStream, uint16_t aStreamType)
+{
+ nsNPAPIPluginStreamListener* streamListener = GetStreamListener(aStream);
+ if (!streamListener) {
+ return false;
+ }
+ return streamListener->SetStreamType(aStreamType);
+}
+
+void
+PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance)
+{
+ if (!mDestroyPending) {
+ // If NPP_Destroy has already been called then these streams have already
+ // been cleaned up on the browser side and are no longer valid.
+ for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
+ PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
+ uint16_t streamType = NP_NORMAL;
+ NPError curError = aInstance->NPP_NewStream(
+ const_cast<char*>(NullableStringGet(curPendingCall.mType)),
+ curPendingCall.mStream, curPendingCall.mSeekable,
+ &streamType);
+ if (curError != NPERR_NO_ERROR) {
+ // If we failed here then the send failed and we need to clean up
+ DestroyAsyncStream(curPendingCall.mStream);
+ }
+ }
+ }
+ mPendingNewStreamCalls.Clear();
+ mInstantiated = true;
+}
+
+/**
+ * During asynchronous initialization it might be necessary to wait for the
+ * plugin to complete its initialization. This typically occurs when the result
+ * of a plugin call depends on the plugin being fully instantiated. For example,
+ * if some JS calls into the plugin, the call must be executed synchronously to
+ * preserve correctness.
+ *
+ * This function works by pumping the plugin's IPC channel for events until
+ * initialization has completed.
+ */
+bool
+PluginAsyncSurrogate::WaitForInit()
+{
+ if (mInitCancelled) {
+ return false;
+ }
+ if (mAcceptCalls) {
+ return true;
+ }
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGINASYNCSURROGATE_WAITFORINIT_MS>
+ timer(mParent->GetHistogramKey());
+ bool result = false;
+ MOZ_ASSERT(mParent);
+ if (mParent->IsChrome()) {
+ PluginProcessParent* process = static_cast<PluginModuleChromeParent*>(mParent)->Process();
+ MOZ_ASSERT(process);
+ process->SetCallRunnableImmediately(true);
+ if (!process->WaitUntilConnected()) {
+ return false;
+ }
+ }
+ if (!mParent->WaitForIPCConnection()) {
+ return false;
+ }
+ if (!mParent->IsChrome()) {
+ // For e10s content processes, we need to spin the content channel until the
+ // protocol bridging has occurred.
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ mozilla::ipc::MessageChannel* contentChannel = cp->GetIPCChannel();
+ MOZ_ASSERT(contentChannel);
+ while (!mParent->mNPInitialized) {
+ if (mParent->mShutdown) {
+ // Since we are pumping the message channel for events, it may be
+ // possible for module initialization to fail during this loop. We must
+ // return false if this happens or else we'll be permanently stuck.
+ return false;
+ }
+ result = contentChannel->WaitForIncomingMessage();
+ if (!result) {
+ return result;
+ }
+ }
+ }
+ mozilla::ipc::MessageChannel* channel = mParent->GetIPCChannel();
+ MOZ_ASSERT(channel);
+ while (!mAcceptCalls) {
+ if (mInitCancelled) {
+ // Since we are pumping the message channel for events, it may be
+ // possible for plugin instantiation to fail during this loop. We must
+ // return false if this happens or else we'll be permanently stuck.
+ return false;
+ }
+ result = channel->WaitForIncomingMessage();
+ if (!result) {
+ break;
+ }
+ }
+ return result;
+}
+
+void
+PluginAsyncSurrogate::AsyncCallDeparting()
+{
+ ++mAsyncCallsInFlight;
+ if (!mPluginDestructionGuard) {
+ mPluginDestructionGuard = MakeUnique<PluginDestructionGuard>(this);
+ }
+}
+
+void
+PluginAsyncSurrogate::AsyncCallArriving()
+{
+ MOZ_ASSERT(mAsyncCallsInFlight > 0);
+ if (--mAsyncCallsInFlight == 0) {
+ mPluginDestructionGuard.reset(nullptr);
+ }
+}
+
+void
+PluginAsyncSurrogate::NotifyAsyncInitFailed()
+{
+ if (!mDestroyPending) {
+ // Clean up any pending NewStream requests
+ for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
+ PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
+ DestroyAsyncStream(curPendingCall.mStream);
+ }
+ }
+ mPendingNewStreamCalls.Clear();
+
+ // Make sure that any WaitForInit calls on this surrogate will fail, or else
+ // we'll be perma-blocked
+ mInitCancelled = true;
+
+ if (!mInstance) {
+ return;
+ }
+ nsPluginInstanceOwner* owner = mInstance->GetOwner();
+ if (owner) {
+ owner->NotifyHostAsyncInitFailed();
+ }
+}
+
+// static
+NPObject*
+PluginAsyncSurrogate::ScriptableAllocate(NPP aInstance, NPClass* aClass)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aClass != GetClass()) {
+ NS_ERROR("Huh?! Wrong class!");
+ return nullptr;
+ }
+
+ return new AsyncNPObject(Cast(aInstance));
+}
+
+// static
+void
+PluginAsyncSurrogate::ScriptableInvalidate(NPObject* aObject)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return;
+ }
+ realObject->_class->invalidate(realObject);
+}
+
+// static
+void
+PluginAsyncSurrogate::ScriptableDeallocate(NPObject* aObject)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ delete object;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableHasMethod(NPObject* aObject,
+ NPIdentifier aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ RecursionGuard guard;
+ if (guard.IsRecursive()) {
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ MOZ_ASSERT(object);
+ bool checkPluginObject = !object->mSurrogate->mInstantiated &&
+ !object->mSurrogate->mAcceptCalls;
+
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ bool result = realObject->_class->hasMethod(realObject, aName);
+ if (!result && checkPluginObject) {
+ // We may be calling into this object because properties in the WebIDL
+ // object hadn't been set yet. Now that we're further along in
+ // initialization, we should try again.
+ const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
+ NPObject* pluginObject = nullptr;
+ NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(),
+ NPNVPluginElementNPObject,
+ (void*)&pluginObject);
+ if (nperror == NPERR_NO_ERROR) {
+ NPPAutoPusher nppPusher(object->mSurrogate->GetNPP());
+ result = pluginObject->_class->hasMethod(pluginObject, aName);
+ npn->releaseobject(pluginObject);
+ NPUTF8* idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ }
+ }
+ return result;
+}
+
+bool
+PluginAsyncSurrogate::GetPropertyHelper(NPObject* aObject, NPIdentifier aName,
+ bool* aHasProperty, bool* aHasMethod,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ if (!aObject) {
+ return false;
+ }
+
+ RecursionGuard guard;
+ if (guard.IsRecursive()) {
+ return false;
+ }
+
+ if (!WaitForInit()) {
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ PluginScriptableObjectParent* actor =
+ static_cast<ParentNPObject*>(realObject)->parent;
+ if (!actor) {
+ return false;
+ }
+ bool success = actor->GetPropertyHelper(aName, aHasProperty, aHasMethod, aResult);
+ if (!success) {
+ const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
+ NPObject* pluginObject = nullptr;
+ NPError nperror = npn->getvalue(GetNPP(), NPNVPluginElementNPObject,
+ (void*)&pluginObject);
+ if (nperror == NPERR_NO_ERROR) {
+ NPPAutoPusher nppPusher(GetNPP());
+ bool hasProperty = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
+ NPUTF8* idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ bool hasMethod = false;
+ if (hasProperty) {
+ hasMethod = pluginObject->_class->hasMethod(pluginObject, aName);
+ success = pluginObject->_class->getProperty(pluginObject, aName, aResult);
+ idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ }
+ *aHasProperty = hasProperty;
+ *aHasMethod = hasMethod;
+ npn->releaseobject(pluginObject);
+ }
+ }
+ return success;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableInvoke(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->invoke(realObject, aName, aArgs, aArgCount, aResult);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableInvokeDefault(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->invokeDefault(realObject, aArgs, aArgCount, aResult);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableHasProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ RecursionGuard guard;
+ if (guard.IsRecursive()) {
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ MOZ_ASSERT(object);
+ bool checkPluginObject = !object->mSurrogate->mInstantiated &&
+ !object->mSurrogate->mAcceptCalls;
+
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ bool result = realObject->_class->hasProperty(realObject, aName);
+ const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
+ NPUTF8* idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ if (!result && checkPluginObject) {
+ // We may be calling into this object because properties in the WebIDL
+ // object hadn't been set yet. Now that we're further along in
+ // initialization, we should try again.
+ NPObject* pluginObject = nullptr;
+ NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(),
+ NPNVPluginElementNPObject,
+ (void*)&pluginObject);
+ if (nperror == NPERR_NO_ERROR) {
+ NPPAutoPusher nppPusher(object->mSurrogate->GetNPP());
+ result = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
+ npn->releaseobject(pluginObject);
+ idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ }
+ }
+ return result;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableGetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ // See GetPropertyHelper below.
+ NS_NOTREACHED("Shouldn't ever call this directly!");
+ return false;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableSetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aValue)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->setProperty(realObject, aName, aValue);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableRemoveProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->removeProperty(realObject, aName);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableEnumerate(NPObject* aObject,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->enumerate(realObject, aIdentifiers, aCount);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableConstruct(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->construct(realObject, aArgs, aArgCount, aResult);
+}
+
+const NPClass PluginAsyncSurrogate::sNPClass = {
+ NP_CLASS_STRUCT_VERSION,
+ PluginAsyncSurrogate::ScriptableAllocate,
+ PluginAsyncSurrogate::ScriptableDeallocate,
+ PluginAsyncSurrogate::ScriptableInvalidate,
+ PluginAsyncSurrogate::ScriptableHasMethod,
+ PluginAsyncSurrogate::ScriptableInvoke,
+ PluginAsyncSurrogate::ScriptableInvokeDefault,
+ PluginAsyncSurrogate::ScriptableHasProperty,
+ PluginAsyncSurrogate::ScriptableGetProperty,
+ PluginAsyncSurrogate::ScriptableSetProperty,
+ PluginAsyncSurrogate::ScriptableRemoveProperty,
+ PluginAsyncSurrogate::ScriptableEnumerate,
+ PluginAsyncSurrogate::ScriptableConstruct
+};
+
+PushSurrogateAcceptCalls::PushSurrogateAcceptCalls(PluginInstanceParent* aInstance)
+ : mSurrogate(nullptr)
+ , mPrevAcceptCallsState(false)
+{
+ MOZ_ASSERT(aInstance);
+ mSurrogate = aInstance->GetAsyncSurrogate();
+ if (mSurrogate) {
+ mPrevAcceptCallsState = mSurrogate->SetAcceptingCalls(true);
+ }
+}
+
+PushSurrogateAcceptCalls::~PushSurrogateAcceptCalls()
+{
+ if (mSurrogate) {
+ mSurrogate->SetAcceptingCalls(mPrevAcceptCallsState);
+ }
+}
+
+} // namespace plugins
+} // namespace mozilla