Bug 521377 - 'NPRuntime: Segfault when NPP_GetValue_NPPVpluginScriptableNPObject returns a null actor'. r=bsmedberg+valgrind.
authorBen Turner <bent.mozilla@gmail.com>
Fri, 15 Jan 2010 12:35:57 -0800
changeset 37311 8df73bd343042881d3a1a4444b0bc7b4048784da
parent 37310 b649980be53f6360515ae0d79e0aa139afc2bd6f
child 37312 dc5c1aad29be2e4fb3fbdc3866a91ace0e72729e
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs521377
milestone1.9.3a1pre
Bug 521377 - 'NPRuntime: Segfault when NPP_GetValue_NPPVpluginScriptableNPObject returns a null actor'. r=bsmedberg+valgrind.
dom/plugins/Makefile.in
dom/plugins/PPluginInstance.ipdl
dom/plugins/PPluginScriptableObject.ipdl
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceChild.h
dom/plugins/PluginInstanceParent.cpp
dom/plugins/PluginInstanceParent.h
dom/plugins/PluginMessageUtils.cpp
dom/plugins/PluginMessageUtils.h
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleChild.h
dom/plugins/PluginModuleParent.cpp
dom/plugins/PluginScriptableObjectChild.cpp
dom/plugins/PluginScriptableObjectChild.h
dom/plugins/PluginScriptableObjectParent.cpp
dom/plugins/PluginScriptableObjectParent.h
dom/plugins/PluginScriptableObjectUtils.cpp
dom/plugins/PluginScriptableObjectUtils.h
modules/plugin/base/src/nsNPAPIPlugin.cpp
--- a/dom/plugins/Makefile.in
+++ b/dom/plugins/Makefile.in
@@ -64,16 +64,17 @@ EXPORTS_mozilla/plugins = \
   PluginInstanceChild.h \
   PluginInstanceParent.h \
   PluginMessageUtils.h \
   PluginModuleChild.h \
   PluginModuleParent.h \
   PluginProcessParent.h \
   PluginScriptableObjectChild.h \
   PluginScriptableObjectParent.h \
+  PluginScriptableObjectUtils.h \
   PluginInstanceChild.h \
   PluginInstanceParent.h \
   AStream.h \
   BrowserStreamChild.h \
   BrowserStreamParent.h \
   PluginStreamChild.h \
   PluginStreamParent.h \
   PluginMessageUtils.h \
@@ -94,16 +95,17 @@ CPPSRCS = \
   PluginMessageUtils.cpp \
   PluginInstanceChild.cpp \
   PluginInstanceParent.cpp \
   PluginModuleChild.cpp \
   PluginModuleParent.cpp \
   PluginProcessParent.cpp \
   PluginScriptableObjectChild.cpp \
   PluginScriptableObjectParent.cpp \
+  PluginScriptableObjectUtils.cpp \
   BrowserStreamChild.cpp \
   BrowserStreamParent.cpp \
   PluginStreamChild.cpp \
   PluginStreamParent.cpp \
   PluginThreadChild.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
--- a/dom/plugins/PPluginInstance.ipdl
+++ b/dom/plugins/PPluginInstance.ipdl
@@ -59,18 +59,17 @@ rpc protocol PPluginInstance
   manager PPluginModule;
 
   manages PPluginScriptableObject;
   manages PBrowserStream;
   manages PPluginStream;
   manages PStreamNotify;
 
 child:
-  rpc __delete__()
-    returns (NPError rv);
+  rpc __delete__();
 
   rpc NPP_SetWindow(NPRemoteWindow window)
     returns (NPError rv);
 
   rpc NPP_GetValue_NPPVpluginWindow()
     returns (bool value, NPError result);
   rpc NPP_GetValue_NPPVpluginTransparent()
     returns (bool value, NPError result);
@@ -78,16 +77,19 @@ child:
   rpc NPP_GetValue_NPPVpluginNeedsXEmbed()
     returns (bool value, NPError result);
   rpc NPP_GetValue_NPPVpluginScriptableNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
 
   rpc NPP_HandleEvent(NPRemoteEvent event)
     returns (int16_t handled);
 
+  rpc NPP_Destroy()
+    returns (NPError rv);
+
 parent:
   rpc NPN_GetValue_NPNVjavascriptEnabledBool()
     returns (bool value, NPError result);
   rpc NPN_GetValue_NPNVisOfflineBool()
     returns (bool value, NPError result);
   rpc NPN_GetValue_NPNVWindowNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
   rpc NPN_GetValue_NPNVPluginElementNPObject()
--- a/dom/plugins/PPluginScriptableObject.ipdl
+++ b/dom/plugins/PPluginScriptableObject.ipdl
@@ -67,20 +67,21 @@ rpc protocol PPluginScriptableObject
 both:
   rpc __delete__();
 
 parent:
   rpc NPN_Evaluate(nsCString aScript)
     returns (Variant aResult,
              bool aSuccess);
 
+child:
+  rpc Invalidate();
+
 both:
   // NPClass methods
-  rpc Invalidate();
-
   rpc HasMethod(NPRemoteIdentifier aId)
     returns (bool aHasMethod);
 
   rpc Invoke(NPRemoteIdentifier aId,
              Variant[] aArgs)
     returns (Variant aResult,
              bool aSuccess);
 
@@ -104,12 +105,22 @@ both:
 
   rpc Enumerate()
     returns (NPRemoteIdentifier[] aProperties,
              bool aSuccess);
 
   rpc Construct(Variant[] aArgs)
     returns (Variant aResult,
              bool aSuccess);
+
+  // Objects are initially unprotected, and the Protect and Unprotect functions
+  // only affect protocol objects that represent NPObjects created in the same
+  // process (rather than protocol objects that are a proxy for an NPObject
+  // created in another process). Protocol objects representing local NPObjects
+  // are protected after an NPObject has been associated with the protocol
+  // object. Sending the protocol object as an argument to the other process
+  // temporarily protects the protocol object again for the duration of the call.
+  rpc Protect();
+  rpc Unprotect();
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -58,52 +58,45 @@ using namespace mozilla::plugins;
 using mozilla::gfx::SharedDIB;
 
 #include <windows.h>
 
 #define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
 #endif
 
 PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface) :
-        mPluginIface(aPluginIface)
+    mPluginIface(aPluginIface)
 #if defined(OS_WIN)
-        , mPluginWindowHWND(0)
-        , mPluginWndProc(0)
-        , mPluginParentHWND(0)
-#endif
-    {
-        memset(&mWindow, 0, sizeof(mWindow));
-        mData.ndata = (void*) this;
+    , mPluginWindowHWND(0)
+    , mPluginWndProc(0)
+    , mPluginParentHWND(0)
+#endif // OS_WIN
+{
+    memset(&mWindow, 0, sizeof(mWindow));
+    mData.ndata = (void*) this;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
-        mWindow.ws_info = &mWsInfo;
-        memset(&mWsInfo, 0, sizeof(mWsInfo));
-#  ifdef MOZ_WIDGET_GTK2
-        mWsInfo.display = GDK_DISPLAY();
-#  endif
-#endif
+    mWindow.ws_info = &mWsInfo;
+    memset(&mWsInfo, 0, sizeof(mWsInfo));
+#ifdef MOZ_WIDGET_GTK2
+    mWsInfo.display = GDK_DISPLAY();
+#endif // MOZ_WIDGET_GTK2
+#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
 #if defined(OS_WIN)
     memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
     mAlphaExtract.doublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
-#endif
-    }
+#endif // OS_WIN
+}
 
 PluginInstanceChild::~PluginInstanceChild()
 {
 #if defined(OS_WIN)
   DestroyPluginWindow();
 #endif
 }
 
-bool
-PluginInstanceChild::Answer__delete__(NPError* rv)
-{
-    return static_cast<PluginModuleChild*>(Manager())->
-        PluginInstanceDestroyed(this, rv);
-}
-
 NPError
 PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
                                   void* aValue)
 {
     PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
     AssertPluginThread();
 
     switch(aVar) {
@@ -169,17 +162,17 @@ PluginInstanceChild::NPN_GetValue(NPNVar
 
         if (result != NPERR_NO_ERROR) {
             return result;
         }
 
         NS_ASSERTION(actor, "Null actor!");
 
         NPObject* object =
-            static_cast<PluginScriptableObjectChild*>(actor)->GetObject();
+            static_cast<PluginScriptableObjectChild*>(actor)->GetObject(true);
         NS_ASSERTION(object, "Null object?!");
 
         PluginModuleChild::sBrowserFuncs.retainobject(object);
         *((NPObject**)aValue) = object;
         return NPERR_NO_ERROR;
     }
 
     case NPNVPluginElementNPObject: {
@@ -192,17 +185,17 @@ PluginInstanceChild::NPN_GetValue(NPNVar
 
         if (result != NPERR_NO_ERROR) {
             return result;
         }
 
         NS_ASSERTION(actor, "Null actor!");
 
         NPObject* object =
-            static_cast<PluginScriptableObjectChild*>(actor)->GetObject();
+            static_cast<PluginScriptableObjectChild*>(actor)->GetObject(true);
         NS_ASSERTION(object, "Null object?!");
 
         PluginModuleChild::sBrowserFuncs.retainobject(object);
         *((NPObject**)aValue) = object;
         return NPERR_NO_ERROR;
     }
 
     case NPNVnetscapeWindow: {
@@ -524,40 +517,16 @@ PluginInstanceChild::AnswerNPP_SetWindow
 }
 
 bool
 PluginInstanceChild::Initialize()
 {
     return true;
 }
 
-void
-PluginInstanceChild::Destroy()
-{
-    // Copy the actors here so we don't enumerate a mutating array.
-    nsAutoTArray<PluginScriptableObjectChild*, 10> objects;
-    PRUint32 count = mScriptableObjects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        objects.AppendElement(mScriptableObjects[index]);
-    }
-
-    count = objects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        PluginScriptableObjectChild*& actor = objects[index];
-        NPObject* object = actor->GetObject();
-        if (object->_class == PluginScriptableObjectChild::GetClass()) {
-          PluginScriptableObjectChild::ScriptableInvalidate(object);
-        }
-    }
-
-#if defined(OS_WIN)
-    SharedSurfaceRelease();
-#endif
-}
-
 #if defined(OS_WIN)
 
 static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
 static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty");
 
 // static
 bool
 PluginInstanceChild::RegisterWindowClass()
@@ -880,75 +849,52 @@ PluginInstanceChild::SharedSurfacePaint(
 }
 
 #endif // OS_WIN
 
 PPluginScriptableObjectChild*
 PluginInstanceChild::AllocPPluginScriptableObject()
 {
     AssertPluginThread();
-
-    nsAutoPtr<PluginScriptableObjectChild>* object =
-        mScriptableObjects.AppendElement();
-    NS_ENSURE_TRUE(object, nsnull);
-
-    *object = new PluginScriptableObjectChild();
-    NS_ENSURE_TRUE(*object, nsnull);
-
-    return object->get();
+    return new PluginScriptableObjectChild(Proxy);
 }
 
 bool
 PluginInstanceChild::DeallocPPluginScriptableObject(
     PPluginScriptableObjectChild* aObject)
 {
     AssertPluginThread();
 
-    PluginScriptableObjectChild* object =
+    PluginScriptableObjectChild* actor =
         reinterpret_cast<PluginScriptableObjectChild*>(aObject);
 
-    NPObject* npobject = object->GetObject();
-    if (npobject &&
-        npobject->_class != PluginScriptableObjectChild::GetClass()) {
-        PluginModuleChild::current()->UnregisterNPObject(npobject);
+    NPObject* object = actor->GetObject(false);
+    if (object) {
+        PluginModuleChild::current()->UnregisterNPObject(object);
     }
 
-    PRUint32 count = mScriptableObjects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        if (mScriptableObjects[index] == object) {
-            mScriptableObjects.RemoveElementAt(index);
-            return true;
-        }
-    }
-    NS_NOTREACHED("An actor we don't know about?!");
-    return false;
+    delete actor;
+    return true;
 }
 
 bool
 PluginInstanceChild::AnswerPPluginScriptableObjectConstructor(
                                            PPluginScriptableObjectChild* aActor)
 {
     AssertPluginThread();
 
     // This is only called in response to the parent process requesting the
     // creation of an actor. This actor will represent an NPObject that is
     // created by the browser and returned to the plugin.
-    NPClass* npclass =
-        const_cast<NPClass*>(PluginScriptableObjectChild::GetClass());
-
-    ChildNPObject* object = reinterpret_cast<ChildNPObject*>(
-        PluginModuleChild::sBrowserFuncs.createobject(GetNPP(), npclass));
-    if (!object) {
-        NS_WARNING("Failed to create NPObject!");
-        return false;
-    }
-
     PluginScriptableObjectChild* actor =
         static_cast<PluginScriptableObjectChild*>(aActor);
-    actor->Initialize(const_cast<PluginInstanceChild*>(this), object);
+    NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
+
+    actor->InitializeProxy();
+    NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
 
     return true;
 }
 
 bool
 PluginInstanceChild::AnswerPBrowserStreamConstructor(
     PBrowserStreamChild* aActor,
     const nsCString& url,
@@ -1048,45 +994,40 @@ PluginInstanceChild::DeallocPStreamNotif
     delete notifyData;
     return true;
 }
 
 PluginScriptableObjectChild*
 PluginInstanceChild::GetActorForNPObject(NPObject* aObject)
 {
     AssertPluginThread();
-  NS_ASSERTION(aObject, "Null pointer!");
+    NS_ASSERTION(aObject, "Null pointer!");
 
-  if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
-      // One of ours! It's a browser-provided object.
-      ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
-      NS_ASSERTION(object->parent, "Null actor!");
-      return object->parent;
-  }
+    if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
+        // One of ours! It's a browser-provided object.
+        ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
+        NS_ASSERTION(object->parent, "Null actor!");
+        return object->parent;
+    }
 
-  PluginScriptableObjectChild* actor =
-      PluginModuleChild::current()->GetActorForNPObject(aObject);
-  if (actor) {
-      // Plugin-provided object that we've previously wrapped.
-      return actor;
-  }
+    PluginScriptableObjectChild* actor =
+        PluginModuleChild::current()->GetActorForNPObject(aObject);
+    if (actor) {
+        // Plugin-provided object that we've previously wrapped.
+        return actor;
+    }
 
-  actor = reinterpret_cast<PluginScriptableObjectChild*>(
-      CallPPluginScriptableObjectConstructor());
-  NS_ENSURE_TRUE(actor, nsnull);
-
-  actor->Initialize(this, aObject);
+    actor = new PluginScriptableObjectChild(LocalObject);
+    if (!CallPPluginScriptableObjectConstructor(actor)) {
+        NS_ERROR("Failed to send constructor message!");
+        return nsnull;
+    }
 
-#ifdef DEBUG
-  bool ok =
-#endif
-  PluginModuleChild::current()->RegisterNPObject(aObject, actor);
-  NS_ASSERTION(ok, "Out of memory?");
-
-  return actor;
+    actor->InitializeLocal(aObject);
+    return actor;
 }
 
 NPError
 PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
                                    NPStream** aStream)
 {
     AssertPluginThread();
 
@@ -1118,8 +1059,21 @@ PluginInstanceChild::InvalidateRect(NPRe
                     aInvalidRect->right, aInvalidRect->bottom };
       ::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
       return;
     }
 #endif
 
     SendNPN_InvalidateRect(*aInvalidRect);
 }
+
+bool
+PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+{
+    PluginModuleChild* module = PluginModuleChild::current();
+    bool retval = module->PluginInstanceDestroyed(this, aResult);
+
+#if defined(OS_WIN)
+    SharedSurfaceRelease();
+#endif
+
+    return retval;
+}
--- a/dom/plugins/PluginInstanceChild.h
+++ b/dom/plugins/PluginInstanceChild.h
@@ -66,32 +66,32 @@ class PluginInstanceChild : public PPlug
                                              UINT message,
                                              WPARAM wParam,
                                              LPARAM lParam);
 #endif
 
 protected:
     virtual bool AnswerNPP_SetWindow(const NPRemoteWindow& window, NPError* rv);
 
-    virtual bool Answer__delete__(NPError* rv);
-
-
     virtual bool
     AnswerNPP_GetValue_NPPVpluginWindow(bool* windowed, NPError* rv);
     virtual bool
     AnswerNPP_GetValue_NPPVpluginTransparent(bool* transparent, NPError* rv);
     virtual bool
     AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(bool* needs, NPError* rv);
     virtual bool
     AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
                                                     NPError* result);
 
     virtual bool
     AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled);
 
+    virtual bool
+    AnswerNPP_Destroy(NPError* result);
+
     virtual PPluginScriptableObjectChild*
     AllocPPluginScriptableObject();
 
     virtual bool
     DeallocPPluginScriptableObject(PPluginScriptableObjectChild* aObject);
 
     virtual bool
     AnswerPPluginScriptableObjectConstructor(PPluginScriptableObjectChild* aActor);
@@ -141,17 +141,16 @@ protected:
     DeallocPStreamNotify(PStreamNotifyChild* notifyData);
 
 public:
     PluginInstanceChild(const NPPluginFuncs* aPluginIface);
 
     virtual ~PluginInstanceChild();
 
     bool Initialize();
-    void Destroy();
 
     NPP GetNPP()
     {
         return &mData;
     }
 
     NPError
     NPN_GetValue(NPNVariable aVariable, void* aValue);
@@ -186,26 +185,25 @@ private:
                                              UINT message,
                                              WPARAM wParam,
                                              LPARAM lParam);
 #endif
 
     const NPPluginFuncs* mPluginIface;
     NPP_t mData;
     NPWindow mWindow;
+
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     NPSetWindowCallbackStruct mWsInfo;
 #elif defined(OS_WIN)
     HWND mPluginWindowHWND;
     WNDPROC mPluginWndProc;
     HWND mPluginParentHWND;
 #endif
 
-    nsTArray<nsAutoPtr<PluginScriptableObjectChild> > mScriptableObjects;
-
 #if defined(OS_WIN)
 private:
     // Shared dib rendering management for windowless plugins.
     bool SharedSurfaceSetWindow(const NPRemoteWindow& aWindow, NPError* rv);
     int16_t SharedSurfacePaint(NPEvent& evcopy);
     void SharedSurfaceRelease();
     bool AlphaExtractCacheSetup();
     void AlphaExtractCacheRelease();
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -38,17 +38,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "PluginInstanceParent.h"
 
 #include "BrowserStreamParent.h"
 #include "PluginModuleParent.h"
 #include "PluginStreamParent.h"
 #include "StreamNotifyParent.h"
-
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 
 #if defined(OS_WIN)
 #include <windowsx.h>
 #endif
 
 using namespace mozilla::plugins;
@@ -64,37 +63,50 @@ PluginInstanceParent::PluginInstancePare
 }
 
 PluginInstanceParent::~PluginInstanceParent()
 {
     if (mNPP)
         mNPP->pdata = NULL;
 }
 
-void
+bool
+PluginInstanceParent::Init()
+{
+    return !!mScriptableObjects.Init();
+}
+
+namespace {
+
+PLDHashOperator
+ActorCollect(const void* aKey,
+             PluginScriptableObjectParent* aData,
+             void* aUserData)
+{
+    nsTArray<PluginScriptableObjectParent*>* objects =
+        reinterpret_cast<nsTArray<PluginScriptableObjectParent*>*>(aUserData);
+    return objects->AppendElement(aData) ? PL_DHASH_NEXT : PL_DHASH_STOP;
+}
+
+} // anonymous namespace
+
+NPError
 PluginInstanceParent::Destroy()
 {
-    // Copy the actors here so we don't enumerate a mutating array.
-    nsAutoTArray<PluginScriptableObjectParent*, 10> objects;
-    PRUint32 count = mScriptableObjects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        objects.AppendElement(mScriptableObjects[index]);
-    }
-
-    count = objects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        NPObject* object = objects[index]->GetObject();
-        if (object->_class == PluginScriptableObjectParent::GetClass()) {
-          PluginScriptableObjectParent::ScriptableInvalidate(object);
-        }
+    NPError retval;
+    if (!CallNPP_Destroy(&retval)) {
+        NS_WARNING("Failed to send message!");
+        return NPERR_GENERIC_ERROR;
     }
 
 #if defined(OS_WIN)
     SharedSurfaceRelease();
 #endif
+
+    return retval;
 }
 
 PBrowserStreamParent*
 PluginInstanceParent::AllocPBrowserStream(const nsCString& url,
                                           const uint32_t& length,
                                           const uint32_t& lastmodified,
                                           PStreamNotifyParent* notifyData,
                                           const nsCString& headers,
@@ -452,17 +464,17 @@ PluginInstanceParent::NPP_GetValue(NPPVa
 
         const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
         if (!npn) {
             NS_WARNING("No netscape functions?!");
             return NPERR_GENERIC_ERROR;
         }
 
         NPObject* object =
-            static_cast<PluginScriptableObjectParent*>(actor)->GetObject();
+            static_cast<PluginScriptableObjectParent*>(actor)->GetObject(true);
         NS_ASSERTION(object, "This shouldn't ever be null!");
 
         (*(NPObject**)_retval) = npn->retainobject(object);
         return NPERR_NO_ERROR;
     }
 
     default:
         PR_LOG(gPluginLog, PR_LOG_WARNING,
@@ -581,111 +593,147 @@ PluginInstanceParent::NPP_DestroyStream(
         PPluginStreamParent::Call__delete__(sp, reason, false);
         return NPERR_NO_ERROR;
     }
 }
 
 PPluginScriptableObjectParent*
 PluginInstanceParent::AllocPPluginScriptableObject()
 {
-    nsAutoPtr<PluginScriptableObjectParent>* object =
-        mScriptableObjects.AppendElement();
-    NS_ENSURE_TRUE(object, nsnull);
+    return new PluginScriptableObjectParent(Proxy);
+}
+
+#ifdef DEBUG
+namespace {
+
+struct ActorSearchData
+{
+    PluginScriptableObjectParent* actor;
+    bool found;
+};
 
-    *object = new PluginScriptableObjectParent();
-    NS_ENSURE_TRUE(*object, nsnull);
+PLDHashOperator
+ActorSearch(const void* aKey,
+            PluginScriptableObjectParent* aData,
+            void* aUserData)
+{
+    ActorSearchData* asd = reinterpret_cast<ActorSearchData*>(aUserData);
+    if (asd->actor == aData) {
+        asd->found = true;
+        return PL_DHASH_STOP;
+    }
+    return PL_DHASH_NEXT;
+}
 
-    return object->get();
-}
+} // anonymous namespace
+#endif // DEBUG
 
 bool
 PluginInstanceParent::DeallocPPluginScriptableObject(
                                          PPluginScriptableObjectParent* aObject)
 {
-    PluginScriptableObjectParent* object =
-        reinterpret_cast<PluginScriptableObjectParent*>(aObject);
+    PluginScriptableObjectParent* actor =
+        static_cast<PluginScriptableObjectParent*>(aObject);
 
-    PRUint32 count = mScriptableObjects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        if (mScriptableObjects[index] == object) {
-            mScriptableObjects.RemoveElementAt(index);
-            return true;
-        }
+    NPObject* object = actor->GetObject(false);
+    if (object) {
+        NS_ASSERTION(mScriptableObjects.Get(object, nsnull),
+                     "NPObject not in the hash!");
+        mScriptableObjects.Remove(object);
     }
-    NS_NOTREACHED("An actor we don't know about?!");
-    return false;
+#ifdef DEBUG
+    else {
+        ActorSearchData asd = { actor, false };
+        mScriptableObjects.EnumerateRead(ActorSearch, &asd);
+        NS_ASSERTION(!asd.found, "Actor in the hash with a null NPObject!");
+    }
+#endif
+
+    delete actor;
+    return true;
 }
 
 bool
 PluginInstanceParent::AnswerPPluginScriptableObjectConstructor(
                                           PPluginScriptableObjectParent* aActor)
 {
     // This is only called in response to the child process requesting the
     // creation of an actor. This actor will represent an NPObject that is
     // created by the plugin and returned to the browser.
-    const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
-    if (!npn) {
-        NS_WARNING("No netscape function pointers?!");
-        return false;
-    }
-
-    NPClass* npclass =
-        const_cast<NPClass*>(PluginScriptableObjectParent::GetClass());
+    PluginScriptableObjectParent* actor =
+        static_cast<PluginScriptableObjectParent*>(aActor);
+    NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
 
-    ParentNPObject* object = reinterpret_cast<ParentNPObject*>(
-        npn->createobject(mNPP, npclass));
-    if (!object) {
-        NS_WARNING("Failed to create NPObject!");
-        return false;
-    }
+    actor->InitializeProxy();
+    NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
 
-    static_cast<PluginScriptableObjectParent*>(aActor)->Initialize(
-        const_cast<PluginInstanceParent*>(this), object);
     return true;
 }
 
 void
 PluginInstanceParent::NPP_URLNotify(const char* url, NPReason reason,
                                     void* notifyData)
 {
     PLUGIN_LOG_DEBUG(("%s (%s, %i, %p)",
                       FULLFUNCTION, url, (int) reason, notifyData));
 
     PStreamNotifyParent* streamNotify =
         static_cast<PStreamNotifyParent*>(notifyData);
     PStreamNotifyParent::Call__delete__(streamNotify, reason);
 }
 
+bool
+PluginInstanceParent::RegisterNPObjectForActor(
+                                           NPObject* aObject,
+                                           PluginScriptableObjectParent* aActor)
+{
+    NS_ASSERTION(aObject && aActor, "Null pointers!");
+    NS_ASSERTION(mScriptableObjects.IsInitialized(), "Hash not initialized!");
+    NS_ASSERTION(!mScriptableObjects.Get(aObject, nsnull), "Duplicate entry!");
+    return !!mScriptableObjects.Put(aObject, aActor);
+}
+
+void
+PluginInstanceParent::UnregisterNPObject(NPObject* aObject)
+{
+    NS_ASSERTION(aObject, "Null pointer!");
+    NS_ASSERTION(mScriptableObjects.IsInitialized(), "Hash not initialized!");
+    NS_ASSERTION(mScriptableObjects.Get(aObject, nsnull), "Unknown entry!");
+    mScriptableObjects.Remove(aObject);
+}
+
 PluginScriptableObjectParent*
 PluginInstanceParent::GetActorForNPObject(NPObject* aObject)
 {
     NS_ASSERTION(aObject, "Null pointer!");
 
     if (aObject->_class == PluginScriptableObjectParent::GetClass()) {
         // One of ours!
         ParentNPObject* object = static_cast<ParentNPObject*>(aObject);
         NS_ASSERTION(object->parent, "Null actor!");
         return object->parent;
     }
 
-    PRUint32 count = mScriptableObjects.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-        nsAutoPtr<PluginScriptableObjectParent>& actor =
-            mScriptableObjects[index];
-        if (actor->GetObject() == aObject) {
-            return actor;
-        }
+    PluginScriptableObjectParent* actor;
+    if (mScriptableObjects.Get(aObject, &actor)) {
+        return actor;
     }
 
-    PluginScriptableObjectParent* actor =
-        static_cast<PluginScriptableObjectParent*>(
-            CallPPluginScriptableObjectConstructor());
-    NS_ENSURE_TRUE(actor, nsnull);
+    actor = new PluginScriptableObjectParent(LocalObject);
+    if (!actor) {
+        NS_ERROR("Out of memory!");
+        return nsnull;
+    }
 
-    actor->Initialize(const_cast<PluginInstanceParent*>(this), aObject);
+    if (!CallPPluginScriptableObjectConstructor(actor)) {
+        NS_WARNING("Failed to send constructor message!");
+        return nsnull;
+    }
+
+    actor->InitializeLocal(aObject);
     return actor;
 }
 
 bool
 PluginInstanceParent::AnswerNPN_PushPopupsEnabledState(const bool& aState,
                                                        bool* aSuccess)
 {
     *aSuccess = mNPNIface->pushpopupsenabledstate(mNPP, aState ? 1 : 0);
--- a/dom/plugins/PluginInstanceParent.h
+++ b/dom/plugins/PluginInstanceParent.h
@@ -42,17 +42,18 @@
 #include "mozilla/plugins/PPluginInstanceParent.h"
 #include "mozilla/plugins/PluginScriptableObjectParent.h"
 #if defined(OS_WIN)
 #include "mozilla/gfx/SharedDIBWin.h"
 #endif
 
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
-#include "nsTArray.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
 #include "nsRect.h"
 
 namespace mozilla {
 namespace plugins {
 
 class PBrowserStreamParent;
 class PluginModuleParent;
 
@@ -64,17 +65,18 @@ class PluginInstanceParent : public PPlu
 
 public:
     PluginInstanceParent(PluginModuleParent* parent,
                          NPP npp,
                          const NPNetscapeFuncs* npniface);
 
     virtual ~PluginInstanceParent();
 
-    void Destroy();
+    bool Init();
+    NPError Destroy();
 
     virtual PPluginScriptableObjectParent*
     AllocPPluginScriptableObject();
 
     virtual bool
     AnswerPPluginScriptableObjectConstructor(PPluginScriptableObjectParent* aActor);
 
     virtual bool
@@ -186,16 +188,23 @@ public:
         return mParent;
     }
 
     const NPNetscapeFuncs* GetNPNIface()
     {
         return mNPNIface;
     }
 
+    bool
+    RegisterNPObjectForActor(NPObject* aObject,
+                             PluginScriptableObjectParent* aActor);
+
+    void
+    UnregisterNPObject(NPObject* aObject);
+
     PluginScriptableObjectParent*
     GetActorForNPObject(NPObject* aObject);
 
     NPP
     GetNPP()
     {
       return mNPP;
     }
@@ -206,17 +215,17 @@ private:
                                      NPError* aResult);
 
 private:
     PluginModuleParent* mParent;
     NPP mNPP;
     const NPNetscapeFuncs* mNPNIface;
     NPWindowType mWindowType;
 
-    nsTArray<nsAutoPtr<PluginScriptableObjectParent> > mScriptableObjects;
+    nsDataHashtable<nsVoidPtrHashKey, PluginScriptableObjectParent*> mScriptableObjects;
 
 #if defined(OS_WIN)
 private:
     // Used in rendering windowless plugins in other processes.
     bool SharedSurfaceSetWindow(const NPWindow* aWindow, NPRemoteWindow& aRemoteWindow);
     void SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent);
     void SharedSurfaceAfterPaint(NPEvent* npevent);
     void SharedSurfaceRelease();
--- a/dom/plugins/PluginMessageUtils.cpp
+++ b/dom/plugins/PluginMessageUtils.cpp
@@ -1,101 +1,106 @@
-/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
-/* vim: set sw=2 ts=8 et tw=80 : */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Content App.
- *
- * The Initial Developer of the Original Code is
- *   The Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "PluginMessageUtils.h"
-#include "nsIRunnable.h"
-#include "nsThreadUtils.h"
-
-namespace {
-
-class DeferNPObjectReleaseRunnable : public nsRunnable
-{
-public:
-  DeferNPObjectReleaseRunnable(const NPNetscapeFuncs* f, NPObject* o)
-    : mFuncs(f)
-    , mObject(o)
-  {
-    NS_ASSERTION(o, "no release null objects");
-  }
-
-  NS_IMETHOD Run();
-
-private:
-  const NPNetscapeFuncs* mFuncs;
-  NPObject* mObject;
-};
-
-NS_IMETHODIMP
-DeferNPObjectReleaseRunnable::Run()
-{
-  mFuncs->releaseobject(mObject);
-  return NS_OK;
-}
-
-} // anonymous namespace
-
-namespace mozilla {
-namespace plugins {
-
-PRLogModuleInfo* gPluginLog = PR_NewLogModule("IPCPlugins");
-
-void
-DeferNPObjectLastRelease(const NPNetscapeFuncs* f, NPObject* o)
-{
-  if (!o)
-    return;
-
-  if (o->referenceCount > 1) {
-    f->releaseobject(o);
-    return;
-  }
-
-  NS_DispatchToCurrentThread(new DeferNPObjectReleaseRunnable(f, o));
-}
-
-void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v)
-{
-  if (!NPVARIANT_IS_OBJECT(*v)) {
-    f->releasevariantvalue(v);
-    return;
-  }
-  DeferNPObjectLastRelease(f, v->value.objectValue);
-  VOID_TO_NPVARIANT(*v);
-}
-
-} // namespace plugins
-} // namespace mozilla
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Content App.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "PluginMessageUtils.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+
+#include "PluginInstanceParent.h"
+#include "PluginInstanceChild.h"
+#include "PluginScriptableObjectParent.h"
+#include "PluginScriptableObjectChild.h"
+
+namespace {
+
+class DeferNPObjectReleaseRunnable : public nsRunnable
+{
+public:
+  DeferNPObjectReleaseRunnable(const NPNetscapeFuncs* f, NPObject* o)
+    : mFuncs(f)
+    , mObject(o)
+  {
+    NS_ASSERTION(o, "no release null objects");
+  }
+
+  NS_IMETHOD Run();
+
+private:
+  const NPNetscapeFuncs* mFuncs;
+  NPObject* mObject;
+};
+
+NS_IMETHODIMP
+DeferNPObjectReleaseRunnable::Run()
+{
+  mFuncs->releaseobject(mObject);
+  return NS_OK;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace plugins {
+
+PRLogModuleInfo* gPluginLog = PR_NewLogModule("IPCPlugins");
+
+void
+DeferNPObjectLastRelease(const NPNetscapeFuncs* f, NPObject* o)
+{
+  if (!o)
+    return;
+
+  if (o->referenceCount > 1) {
+    f->releaseobject(o);
+    return;
+  }
+
+  NS_DispatchToCurrentThread(new DeferNPObjectReleaseRunnable(f, o));
+}
+
+void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v)
+{
+  if (!NPVARIANT_IS_OBJECT(*v)) {
+    f->releasevariantvalue(v);
+    return;
+  }
+  DeferNPObjectLastRelease(f, v->value.objectValue);
+  VOID_TO_NPVARIANT(*v);
+}
+
+} // namespace plugins
+} // namespace mozilla
--- a/dom/plugins/PluginMessageUtils.h
+++ b/dom/plugins/PluginMessageUtils.h
@@ -41,16 +41,17 @@
 
 #include "IPC/IPCMessageUtils.h"
 
 #include "npapi.h"
 #include "npruntime.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsStringGlue.h"
+#include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 namespace mozilla {
 
 // XXX might want to move these to nscore.h or something, they can be
 // generally useful
 struct void_t { };
@@ -59,16 +60,22 @@ struct null_t { };
 namespace ipc {
 
 typedef intptr_t NPRemoteIdentifier;
 
 } /* namespace ipc */
 
 namespace plugins {
 
+enum ScriptableObjectType
+{
+  LocalObject,
+  Proxy
+};
+
 extern PRLogModuleInfo* gPluginLog;
 
 #if defined(_MSC_VER)
 #define FULLFUNCTION __FUNCSIG__
 #elif (__GNUC__ >= 4)
 #define FULLFUNCTION __PRETTY_FUNCTION__
 #else
 #define FULLFUNCTION __FUNCTION__
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -241,18 +241,16 @@ PluginModuleChild::RegisterNPObject(NPOb
 }
 
 void
 PluginModuleChild::UnregisterNPObject(NPObject* aObject)
 {
     AssertPluginThread();
     NS_ASSERTION(mObjectMap.IsInitialized(), "Not initialized!");
     NS_ASSERTION(aObject, "Null pointer!");
-    NS_ASSERTION(mObjectMap.Get(aObject, nsnull),
-                 "Unregistering an object that was never added!");
     mObjectMap.Remove(aObject);
 }
 
 PluginScriptableObjectChild*
 PluginModuleChild::GetActorForNPObject(NPObject* aObject)
 {
     AssertPluginThread();
     NS_ASSERTION(mObjectMap.IsInitialized(), "Not initialized!");
@@ -283,16 +281,22 @@ ActorSearch(const void* aKey,
   }
 
   return PL_DHASH_NEXT;
 }
 
 } // anonymous namespace
 
 bool
+PluginModuleChild::NPObjectIsRegistered(NPObject* aObject)
+{
+    return !!mObjectMap.Get(aObject, nsnull);
+}
+
+bool
 PluginModuleChild::NPObjectIsRegisteredForActor(
                                             PluginScriptableObjectChild* aActor)
 {
     AssertPluginThread();
     NS_ASSERTION(mObjectMap.IsInitialized(), "Not initialized!");
     NS_ASSERTION(aActor, "Null actor!");
 
     SearchInfo info = { aActor, false };
@@ -1471,14 +1475,15 @@ PluginModuleChild::DeallocPPluginInstanc
 
 bool
 PluginModuleChild::PluginInstanceDestroyed(PluginInstanceChild* aActor,
                                            NPError* rv)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
 
-    *rv = mFunctions.destroy(aActor->GetNPP(), 0);
-    aActor->Destroy();
-    aActor->GetNPP()->ndata = 0;
+    NPP npp = aActor->GetNPP();
+
+    *rv = mFunctions.destroy(npp, 0);
+    npp->ndata = 0;
 
     return true;
 }
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -141,16 +141,17 @@ public:
     bool RegisterNPObject(NPObject* aObject,
                           PluginScriptableObjectChild* aActor);
 
     void UnregisterNPObject(NPObject* aObject);
 
     PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
 
 #ifdef DEBUG
+    bool NPObjectIsRegistered(NPObject* aObject);
     bool NPObjectIsRegisteredForActor(PluginScriptableObjectChild* aActor);
 #endif
 
     bool
     PluginInstanceDestroyed(PluginInstanceChild* aActor,
                             NPError* rv);
 
 private:
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -155,40 +155,39 @@ PluginModuleParent::SetPluginFuncs(NPPlu
     aFuncs->event = NPP_HandleEvent;
     aFuncs->urlnotify = NPP_URLNotify;
     aFuncs->getvalue = NPP_GetValue;
     aFuncs->setvalue = NPP_SetValue;
 }
 
 NPError
 PluginModuleParent::NPP_Destroy(NPP instance,
-                                NPSavedData** save)
+                                NPSavedData** /*saved*/)
 {
     // FIXME/cjones:
     //  (1) send a "destroy" message to the child
     //  (2) the child shuts down its instance
     //  (3) remove both parent and child IDs from map
     //  (4) free parent
     PLUGIN_LOG_DEBUG_FUNCTION;
 
     PluginInstanceParent* parentInstance =
         static_cast<PluginInstanceParent*>(instance->pdata);
 
     if (!parentInstance)
         return NPERR_NO_ERROR;
 
-    parentInstance->Destroy();
-
-    NPError prv;
-    if (!PPluginInstanceParent::Call__delete__(parentInstance, &prv)) {
-        prv = NPERR_GENERIC_ERROR;
-    }
+    NPError retval = parentInstance->Destroy();
     instance->pdata = nsnull;
 
-    return prv;
+    if (!PluginInstanceParent::Call__delete__(parentInstance)) {
+        NS_ERROR("Failed to delete instance!");
+    }
+
+    return retval;
 }
 
 bool
 PluginModuleParent::EnsureValidNPIdentifier(NPIdentifier aIdentifier)
 {
     if (!mValidIdentifiers.GetEntry(aIdentifier)) {
         nsVoidPtrHashKey* newEntry = mValidIdentifiers.PutEntry(aIdentifier);
         if (!newEntry) {
@@ -636,35 +635,39 @@ PluginModuleParent::NPP_New(NPMIMEType p
     for (int i = 0; i < argc; ++i) {
         names.AppendElement(NullableString(argn[i]));
         values.AppendElement(NullableString(argv[i]));
     }
 
     PluginInstanceParent* parentInstance =
         new PluginInstanceParent(this, instance, mNPNIface);
 
+    if (!parentInstance->Init()) {
+        delete parentInstance;
+        return NS_ERROR_FAILURE;
+    }
+
     instance->pdata = parentInstance;
 
     if (!CallPPluginInstanceConstructor(parentInstance,
                                         nsDependentCString(pluginType), mode,
                                         names, values, error)) {
         // |parentInstance| is automatically deleted.
         instance->pdata = nsnull;
         // if IPC is down, we'll get an immediate "failed" return, but
         // without *error being set.  So make sure that the error
         // condition is signaled to nsNPAPIPluginInstance
         if (NPERR_NO_ERROR == *error)
             *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
     if (*error != NPERR_NO_ERROR) {
-        PPluginInstanceParent::Call__delete__(parentInstance, error);
-        instance->pdata = nsnull;
-        return NS_ERROR_FAILURE;
+        NPP_Destroy(instance, 0);
+        return *error;
     }
 
     return NS_OK;
 }
 
 bool
 PluginModuleParent::AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
                                                       NPError* aError,
--- a/dom/plugins/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/PluginScriptableObjectChild.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=4 ts=4 et :
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -32,296 +32,139 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "PluginScriptableObjectChild.h"
-
-#include "npapi.h"
-#include "npruntime.h"
-#include "nsDebug.h"
-
-#include "PluginModuleChild.h"
-#include "PluginInstanceChild.h"
+#include "PluginScriptableObjectUtils.h"
 
 using namespace mozilla::plugins;
 using mozilla::ipc::NPRemoteIdentifier;
 
-namespace {
-
-inline NPObject*
-NPObjectFromVariant(const Variant& aRemoteVariant)
-{
-  NS_ASSERTION(aRemoteVariant.type() ==
-               Variant::TPPluginScriptableObjectChild,
-               "Wrong variant type!");
-  PluginScriptableObjectChild* actor =
-    const_cast<PluginScriptableObjectChild*>(
-      reinterpret_cast<const PluginScriptableObjectChild*>(
-        aRemoteVariant.get_PPluginScriptableObjectChild()));
-  return actor->GetObject();
-}
-
-inline NPObject*
-NPObjectFromVariant(const NPVariant& aVariant)
-{
-  NS_ASSERTION(NPVARIANT_IS_OBJECT(aVariant), "Wrong variant type!");
-  return NPVARIANT_TO_OBJECT(aVariant);
-}
-
-void
-ConvertToVariant(const Variant& aRemoteVariant,
-                 NPVariant& aVariant)
-{
-  switch (aRemoteVariant.type()) {
-    case Variant::Tvoid_t: {
-      VOID_TO_NPVARIANT(aVariant);
-      break;
-    }
-
-    case Variant::Tnull_t: {
-      NULL_TO_NPVARIANT(aVariant);
-      break;
-    }
-
-    case Variant::Tbool: {
-      BOOLEAN_TO_NPVARIANT(aRemoteVariant.get_bool(), aVariant);
-      break;
-    }
-
-    case Variant::Tint: {
-      INT32_TO_NPVARIANT(aRemoteVariant.get_int(), aVariant);
-      break;
-    }
-
-    case Variant::Tdouble: {
-      DOUBLE_TO_NPVARIANT(aRemoteVariant.get_double(), aVariant);
-      break;
-    }
-
-    case Variant::TnsCString: {
-      const nsCString& string = aRemoteVariant.get_nsCString();
-      NPUTF8* buffer = reinterpret_cast<NPUTF8*>(strdup(string.get()));
-      NS_ASSERTION(buffer, "Out of memory!");
-      STRINGN_TO_NPVARIANT(buffer, string.Length(), aVariant);
-      break;
-    }
-
-    case Variant::TPPluginScriptableObjectChild: {
-      NPObject* object = NPObjectFromVariant(aRemoteVariant);
-      NS_ASSERTION(object, "Null object?!");
-      PluginModuleChild::sBrowserFuncs.retainobject(object);
-      OBJECT_TO_NPVARIANT(object, aVariant);
-      break;
-    }
-
-    default:
-      NS_RUNTIMEABORT("Shouldn't get here!");
-  }
-}
-
-bool
-ConvertToRemoteVariant(const NPVariant& aVariant,
-                       Variant& aRemoteVariant,
-                       PluginInstanceChild* aInstance)
-{
-  if (NPVARIANT_IS_VOID(aVariant)) {
-    aRemoteVariant = mozilla::void_t();
-  }
-  else if (NPVARIANT_IS_NULL(aVariant)) {
-    aRemoteVariant = mozilla::null_t();
-  }
-  else if (NPVARIANT_IS_BOOLEAN(aVariant)) {
-    aRemoteVariant = NPVARIANT_TO_BOOLEAN(aVariant);
-  }
-  else if (NPVARIANT_IS_INT32(aVariant)) {
-    aRemoteVariant = NPVARIANT_TO_INT32(aVariant);
-  }
-  else if (NPVARIANT_IS_DOUBLE(aVariant)) {
-    aRemoteVariant = NPVARIANT_TO_DOUBLE(aVariant);
-  }
-  else if (NPVARIANT_IS_STRING(aVariant)) {
-    NPString str = NPVARIANT_TO_STRING(aVariant);
-    nsCString string(str.UTF8Characters, str.UTF8Length);
-    aRemoteVariant = string;
-  }
-  else if (NPVARIANT_IS_OBJECT(aVariant)) {
-    NS_ASSERTION(aInstance, "Must have an instance to wrap!");
-
-    NPObject* object = NPVARIANT_TO_OBJECT(aVariant);
-    NS_ASSERTION(object, "Null object?!");
-
-    PluginScriptableObjectChild* actor = aInstance->GetActorForNPObject(object);
-    if (!actor) {
-      NS_ERROR("Failed to create actor!");
-      return false;
-    }
-    aRemoteVariant = actor;
-  }
-  else {
-    NS_NOTREACHED("Shouldn't get here!");
-    return false;
-  }
-
-  return true;
-}
-
-} // anonymous namespace
-
 // static
 NPObject*
 PluginScriptableObjectChild::ScriptableAllocate(NPP aInstance,
                                                 NPClass* aClass)
 {
   AssertPluginThread();
 
-  NS_ASSERTION(aClass == PluginScriptableObjectChild::GetClass(),
-               "Huh?! Wrong class!");
+  if (aClass != GetClass()) {
+    NS_RUNTIMEABORT("Huh?! Wrong class!");
+  }
 
-  ChildNPObject* object = reinterpret_cast<ChildNPObject*>(
-    PluginModuleChild::sBrowserFuncs.memalloc(sizeof(ChildNPObject)));
-  if (object) {
-    memset(object, 0, sizeof(ChildNPObject));
-  }
-  return object;
+  return new ChildNPObject();
 }
 
 // static
 void
 PluginScriptableObjectChild::ScriptableInvalidate(NPObject* aObject)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
-
-  PluginInstanceChild* instance = actor ? actor->GetInstance() : nsnull;
-  NS_WARN_IF_FALSE(instance, "No instance!");
-
-  if (actor && !actor->CallInvalidate()) {
-    NS_WARNING("Failed to send message!");
-  }
-
   object->invalidated = true;
-
-  if (instance &&
-      !PPluginScriptableObjectChild::Call__delete__(object->parent)) {
-    NS_WARNING("Failed to send message!");
-  }
 }
 
 // static
 void
 PluginScriptableObjectChild::ScriptableDeallocate(NPObject* aObject)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return;
+  if (aObject->_class != GetClass()) {
+    NS_RUNTIMEABORT("Don't know what kind of object this is!");
   }
 
   ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
   if (!object->invalidated) {
     ScriptableInvalidate(aObject);
   }
+  NS_ASSERTION(object->invalidated, "Should have invalidated already!");
 
-  NS_ASSERTION(object->invalidated, "Should be invalidated!");
+  PluginScriptableObjectChild* actor = object->parent;
+  if (actor) {
+    NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+    actor->DropNPObject();
+  }
 
-  NS_Free(aObject);
+  delete object;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableHasMethod(NPObject* aObject,
                                                  NPIdentifier aName)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
-  if (!actor->CallHasMethod((NPRemoteIdentifier)aName, &result)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallHasMethod((NPRemoteIdentifier)aName, &result);
 
   return result;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableInvoke(NPObject* aObject,
                                               NPIdentifier aName,
                                               const NPVariant* aArgs,
                                               uint32_t aArgCount,
                                               NPVariant* aResult)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
-  nsAutoTArray<Variant, 10> args;
-  if (!args.SetLength(aArgCount)) {
-    NS_ERROR("Out of memory?!");
+  ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+  if (!args.IsOk()) {
+    NS_ERROR("Failed to convert arguments!");
     return false;
   }
 
-  for (PRUint32 index = 0; index < aArgCount; index++) {
-    Variant& arg = args[index];
-    if (!ConvertToRemoteVariant(aArgs[index], arg, actor->GetInstance())) {
-      NS_WARNING("Failed to convert argument!");
-      return false;
-    }
-  }
-
   Variant remoteResult;
   bool success;
-  if (!actor->CallInvoke((NPRemoteIdentifier)aName, args, &remoteResult,
-                          &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallInvoke((NPRemoteIdentifier)aName, args, &remoteResult, &success);
 
   if (!success) {
     return false;
   }
 
   ConvertToVariant(remoteResult, *aResult);
   return true;
 }
@@ -330,117 +173,100 @@ PluginScriptableObjectChild::ScriptableI
 bool
 PluginScriptableObjectChild::ScriptableInvokeDefault(NPObject* aObject,
                                                      const NPVariant* aArgs,
                                                      uint32_t aArgCount,
                                                      NPVariant* aResult)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
-  nsAutoTArray<Variant, 10> args;
-  if (!args.SetLength(aArgCount)) {
-    NS_ERROR("Out of memory?!");
+  ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+  if (!args.IsOk()) {
+    NS_ERROR("Failed to convert arguments!");
     return false;
   }
 
-  for (PRUint32 index = 0; index < aArgCount; index++) {
-    Variant& arg = args[index];
-    if (!ConvertToRemoteVariant(aArgs[index], arg, actor->GetInstance())) {
-      NS_WARNING("Failed to convert argument!");
-      return false;
-    }
-  }
-
   Variant remoteResult;
   bool success;
-  if (!actor->CallInvokeDefault(args, &remoteResult, &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  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 != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
-  if (!actor->CallHasProperty((NPRemoteIdentifier)aName, &result)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallHasProperty((NPRemoteIdentifier)aName, &result);
 
   return result;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject,
                                                    NPIdentifier aName,
                                                    NPVariant* aResult)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  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;
-  if (!actor->CallGetProperty((NPRemoteIdentifier)aName, &result, &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallGetProperty((NPRemoteIdentifier)aName, &result, &success);
 
   if (!success) {
     return false;
   }
 
   ConvertToVariant(result, *aResult);
   return true;
 }
@@ -448,103 +274,94 @@ PluginScriptableObjectChild::ScriptableG
 // static
 bool
 PluginScriptableObjectChild::ScriptableSetProperty(NPObject* aObject,
                                                    NPIdentifier aName,
                                                    const NPVariant* aValue)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
-  Variant value;
-  if (!ConvertToRemoteVariant(*aValue, value, actor->GetInstance())) {
+  ProtectedVariant value(*aValue, actor->GetInstance());
+  if (!value.IsOk()) {
     NS_WARNING("Failed to convert variant!");
     return false;
   }
 
   bool success;
-  if (!actor->CallSetProperty((NPRemoteIdentifier)aName, value, &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallSetProperty((NPRemoteIdentifier)aName, value, &success);
 
   return success;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableRemoveProperty(NPObject* aObject,
                                                       NPIdentifier aName)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool success;
-  if (!actor->CallRemoveProperty((NPRemoteIdentifier)aName, &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallRemoveProperty((NPRemoteIdentifier)aName, &success);
 
   return success;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject,
                                                  NPIdentifier** aIdentifiers,
                                                  uint32_t* aCount)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   nsAutoTArray<NPRemoteIdentifier, 10> identifiers;
   bool success;
-  if (!actor->CallEnumerate(&identifiers, &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallEnumerate(&identifiers, &success);
 
   if (!success) {
     return false;
   }
 
   *aCount = identifiers.Length();
   if (!*aCount) {
     *aIdentifiers = nsnull;
@@ -568,50 +385,39 @@ PluginScriptableObjectChild::ScriptableE
 bool
 PluginScriptableObjectChild::ScriptableConstruct(NPObject* aObject,
                                                  const NPVariant* aArgs,
                                                  uint32_t aArgCount,
                                                  NPVariant* aResult)
 {
   AssertPluginThread();
 
-  if (aObject->_class != PluginScriptableObjectChild::GetClass()) {
-    NS_ERROR("Don't know what kind of object this is!");
-    return false;
+  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;
   }
 
-  PluginScriptableObjectChild* actor = object->parent;
+  ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
-  nsAutoTArray<Variant, 10> args;
-  if (!args.SetLength(aArgCount)) {
-    NS_ERROR("Out of memory?!");
+  ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+  if (!args.IsOk()) {
+    NS_ERROR("Failed to convert arguments!");
     return false;
   }
 
-  for (PRUint32 index = 0; index < aArgCount; index++) {
-    Variant& arg = args[index];
-    if (!ConvertToRemoteVariant(aArgs[index], arg, actor->GetInstance())) {
-      NS_WARNING("Failed to convert argument!");
-      return false;
-    }
-  }
-
   Variant remoteResult;
   bool success;
-  if (!actor->CallConstruct(args, &remoteResult, &success)) {
-    NS_WARNING("Failed to send message!");
-    return false;
-  }
+  actor->CallConstruct(args, &remoteResult, &success);
 
   if (!success) {
     return false;
   }
 
   ConvertToVariant(remoteResult, *aResult);
   return true;
 }
@@ -627,108 +433,227 @@ const NPClass PluginScriptableObjectChil
   PluginScriptableObjectChild::ScriptableHasProperty,
   PluginScriptableObjectChild::ScriptableGetProperty,
   PluginScriptableObjectChild::ScriptableSetProperty,
   PluginScriptableObjectChild::ScriptableRemoveProperty,
   PluginScriptableObjectChild::ScriptableEnumerate,
   PluginScriptableObjectChild::ScriptableConstruct
 };
 
-PluginScriptableObjectChild::PluginScriptableObjectChild()
+PluginScriptableObjectChild::PluginScriptableObjectChild(
+                                                     ScriptableObjectType aType)
 : mInstance(nsnull),
-  mObject(nsnull)
+  mObject(nsnull),
+  mInvalidated(false),
+  mProtectCount(0),
+  mType(aType)
 {
   AssertPluginThread();
 }
 
 PluginScriptableObjectChild::~PluginScriptableObjectChild()
 {
   AssertPluginThread();
 
   if (mObject) {
     if (mObject->_class == GetClass()) {
-      if (!static_cast<ChildNPObject*>(mObject)->invalidated) {
-        NS_WARNING("This should have happened already!");
-        ScriptableInvalidate(mObject);
-      }
+      NS_ASSERTION(mType == Proxy, "Wrong type!");
+      static_cast<ChildNPObject*>(mObject)->parent = nsnull;
     }
     else {
-      // Make sure we've invalidated our NPObject so that the plugin doesn't
-      // hold an object with a dangling pointer.
-
-      // Calling a virtual in the destructor, make sure we call the right one.
-      PluginScriptableObjectChild::AnswerInvalidate();
+      NS_ASSERTION(mType == LocalObject, "Wrong type!");
+      PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
     }
   }
+
   NS_ASSERTION(!PluginModuleChild::current()->
                NPObjectIsRegisteredForActor(this),
                "NPObjects still registered for this actor!");
 }
 
 void
-PluginScriptableObjectChild::Initialize(PluginInstanceChild* aInstance,
-                                        NPObject* aObject)
+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();
+  NS_ASSERTION(object, "Failed to create object!");
+
+  if (!PluginModuleChild::current()->RegisterNPObject(object, this)) {
+    NS_ERROR("Out of memory?");
+  }
+
+  mObject = object;
+}
+
+void
+PluginScriptableObjectChild::InitializeLocal(NPObject* aObject)
 {
   AssertPluginThread();
-
-  NS_ASSERTION(!(mInstance && mObject), "Calling Initialize class twice!");
+  NS_ASSERTION(mType == LocalObject, "Bad type!");
+  NS_ASSERTION(!mObject, "Calling Initialize more than once!");
+  NS_ASSERTION(!mInvalidated, "Already invalidated?!");
 
-  if (aObject->_class == GetClass()) {
-    ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
-
-    NS_ASSERTION(!object->parent, "Bad object!");
-    object->parent = const_cast<PluginScriptableObjectChild*>(this);
+  mInstance = static_cast<PluginInstanceChild*>(Manager());
+  NS_ASSERTION(mInstance, "Null manager?!");
 
-    // 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 parent.
-    NS_ASSERTION(aObject->referenceCount == 1, "Some kind of live object!");
-    aObject->referenceCount = 0;
-    NS_LOG_RELEASE(aObject, 0, "ChildNPObject");
-  }
-  else {
-    // Plugin-provided object, retain here. This should be the only reference we
-    // ever need.
-    PluginModuleChild::sBrowserFuncs.retainobject(aObject);
+  PluginModuleChild::sBrowserFuncs.retainobject(aObject);
+
+  NS_ASSERTION(!mProtectCount, "Should be zero!");
+  mProtectCount++;
+
+  if (!PluginModuleChild::current()->RegisterNPObject(aObject, this)) {
+      NS_ERROR("Out of memory?");
   }
 
-  mInstance = aInstance;
   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, "ChildNPObject");
+
+  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!");
+
+  NPObject* object = CreateProxyObject();
+  if (!object) {
+    NS_WARNING("Failed to create object!");
+    return false;
+  }
+
+  InitializeProxy();
+  NS_ASSERTION(mObject, "Initialize failed!");
+
+  CallProtect();
+  return true;
+}
+
+NPObject*
+PluginScriptableObjectChild::GetObject(bool aCanResurrect)
+{
+  if (!mObject && aCanResurrect && !ResurrectProxyObject()) {
+    NS_ERROR("Null object!");
+    return nsnull;
+  }
+  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::Call__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.
+  PluginModuleChild::current()->UnregisterNPObject(mObject);
+  mObject = nsnull;
+
+  CallUnprotect();
+}
+
 bool
 PluginScriptableObjectChild::AnswerInvalidate()
 {
   AssertPluginThread();
 
-  if (mObject) {
-    NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
-    if (mObject->_class && mObject->_class->invalidate) {
-      mObject->_class->invalidate(mObject);
-    }
-    PluginModuleChild::current()->UnregisterNPObject(mObject);
-    PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
-    mObject = nsnull;
+  if (mInvalidated) {
+    NS_WARNING("Called invalidate more than once?!");
+    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);
+  }
+
+  PluginModuleChild::current()->UnregisterNPObject(mObject);
+  Unprotect();
+
   return true;
 }
 
 bool
 PluginScriptableObjectChild::AnswerHasMethod(const NPRemoteIdentifier& aId,
                                              bool* aHasMethod)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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;
   }
 
   *aHasMethod = mObject->_class->hasMethod(mObject, (NPIdentifier)aId);
   return true;
@@ -737,24 +662,25 @@ PluginScriptableObjectChild::AnswerHasMe
 bool
 PluginScriptableObjectChild::AnswerInvoke(const NPRemoteIdentifier& aId,
                                           const nsTArray<Variant>& aArgs,
                                           Variant* aResult,
                                           bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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;
   }
 
   nsAutoTArray<NPVariant, 10> convertedArgs;
@@ -781,17 +707,18 @@ PluginScriptableObjectChild::AnswerInvok
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
-  success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+  success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
+                                   false);
 
   DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -803,24 +730,25 @@ PluginScriptableObjectChild::AnswerInvok
 
 bool
 PluginScriptableObjectChild::AnswerInvokeDefault(const nsTArray<Variant>& aArgs,
                                                  Variant* aResult,
                                                  bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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;
   }
 
   nsAutoTArray<NPVariant, 10> convertedArgs;
@@ -847,17 +775,18 @@ PluginScriptableObjectChild::AnswerInvok
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
-  success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+  success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
+                                   false);
 
   DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -868,23 +797,24 @@ PluginScriptableObjectChild::AnswerInvok
 }
 
 bool
 PluginScriptableObjectChild::AnswerHasProperty(const NPRemoteIdentifier& aId,
                                                bool* aHasProperty)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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;
   }
 
   *aHasProperty = mObject->_class->hasProperty(mObject, (NPIdentifier)aId);
   return true;
@@ -892,40 +822,42 @@ PluginScriptableObjectChild::AnswerHasPr
 
 bool
 PluginScriptableObjectChild::AnswerGetProperty(const NPRemoteIdentifier& aId,
                                                Variant* aResult,
                                                bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  if (mInvalidated) {
     NS_WARNING("Calling AnswerGetProperty 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->getProperty)) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   NPVariant result;
   if (!mObject->_class->getProperty(mObject, (NPIdentifier)aId, &result)) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant converted;
-  if ((*aSuccess = ConvertToRemoteVariant(result, converted, GetInstance()))) {
+  if ((*aSuccess = ConvertToRemoteVariant(result, converted, GetInstance(),
+                                          false))) {
     DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
     *aResult = converted;
   }
   else {
     *aResult = void_t();
   }
 
   return true;
@@ -933,23 +865,24 @@ PluginScriptableObjectChild::AnswerGetPr
 
 bool
 PluginScriptableObjectChild::AnswerSetProperty(const NPRemoteIdentifier& aId,
                                                const Variant& aValue,
                                                bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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->setProperty)) {
     *aSuccess = false;
     return true;
   }
 
   NPVariant converted;
   ConvertToVariant(aValue, converted);
@@ -962,46 +895,48 @@ PluginScriptableObjectChild::AnswerSetPr
 }
 
 bool
 PluginScriptableObjectChild::AnswerRemoveProperty(const NPRemoteIdentifier& aId,
                                                   bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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->removeProperty)) {
     *aSuccess = false;
     return true;
   }
 
   *aSuccess = mObject->_class->removeProperty(mObject, (NPIdentifier)aId);
   return true;
 }
 
 bool
 PluginScriptableObjectChild::AnswerEnumerate(nsTArray<NPRemoteIdentifier>* aProperties,
                                              bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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;
@@ -1031,24 +966,25 @@ PluginScriptableObjectChild::AnswerEnume
 
 bool
 PluginScriptableObjectChild::AnswerConstruct(const nsTArray<Variant>& aArgs,
                                              Variant* aResult,
                                              bool* aSuccess)
 {
   AssertPluginThread();
 
-  if (!mObject) {
+  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;
   }
 
   nsAutoTArray<NPVariant, 10> convertedArgs;
@@ -1074,41 +1010,64 @@ PluginScriptableObjectChild::AnswerConst
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
-  success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+  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::AnswerProtect()
+{
+  NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+  NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+  Protect();
+  return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerUnprotect()
+{
+  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;
-  if (!(CallNPN_Evaluate(script, &result, &success) && success)) {
+  CallNPN_Evaluate(script, &result, &success);
+
+  if (!success) {
     return false;
   }
 
   ConvertToVariant(result, *aResult);
   return true;
 }
--- a/dom/plugins/PluginScriptableObjectChild.h
+++ b/dom/plugins/PluginScriptableObjectChild.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: sw=4 ts=4 et :
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -46,28 +46,40 @@
 namespace mozilla {
 namespace plugins {
 
 class PluginInstanceChild;
 class PluginScriptableObjectChild;
 
 struct ChildNPObject : NPObject
 {
+  ChildNPObject()
+    : NPObject(), parent(NULL), invalidated(false) { }
+
+  // |parent| is always valid as long as the actor is alive. Once the actor is
+  // destroyed this will be set to null.
   PluginScriptableObjectChild* parent;
   bool invalidated;
 };
 
 class PluginScriptableObjectChild : public PPluginScriptableObjectChild
 {
   friend class PluginInstanceChild;
 
 public:
-  PluginScriptableObjectChild();
+  PluginScriptableObjectChild(ScriptableObjectType aType);
   virtual ~PluginScriptableObjectChild();
 
+  void
+  InitializeProxy();
+
+  void
+  InitializeLocal(NPObject* aObject);
+
+
   virtual bool
   AnswerInvalidate();
 
   virtual bool
   AnswerHasMethod(const NPRemoteIdentifier& aId,
                   bool* aHasMethod);
 
   virtual bool
@@ -103,42 +115,65 @@ public:
   AnswerEnumerate(nsTArray<NPRemoteIdentifier>* aProperties,
                   bool* aSuccess);
 
   virtual bool
   AnswerConstruct(const nsTArray<Variant>& aArgs,
                   Variant* aResult,
                   bool* aSuccess);
 
-  void
-  Initialize(PluginInstanceChild* aInstance,
-             NPObject* aObject);
+  virtual bool
+  AnswerProtect();
+
+  virtual bool
+  AnswerUnprotect();
 
   NPObject*
-  GetObject()
-  {
-    return mObject;
-  }
+  GetObject(bool aCanResurrect);
 
   static const NPClass*
   GetClass()
   {
     return &sNPClass;
   }
 
   PluginInstanceChild*
-  GetInstance()
+  GetInstance() const
   {
     return mInstance;
   }
 
+  // Protect only affects LocalObject actors. It is called by the
+  // ProtectedVariant/Actor helper classes before the actor is used as an
+  // argument to an IPC call and when the parent process resurrects a
+  // proxy object to the NPObject associated with this actor.
+  void Protect();
+
+  // Unprotect only affects LocalObject actors. It is called by the
+  // ProtectedVariant/Actor helper classes after the actor is used as an
+  // argument to an IPC call and when the parent process is no longer using
+  // this actor.
+  void Unprotect();
+
+  // DropNPObject is only used for Proxy actors and is called when the child
+  // process is no longer using the NPObject associated with this actor. The
+  // parent process may subsequently use this actor again in which case a new
+  // NPObject will be created and associated with this actor (see
+  // ResurrectProxyObject).
+  void DropNPObject();
+
   bool
   Evaluate(NPString* aScript,
            NPVariant* aResult);
 
+  ScriptableObjectType
+  Type() const {
+    return mType;
+  }
+
 private:
   static NPObject*
   ScriptableAllocate(NPP aInstance,
                      NPClass* aClass);
 
   static void
   ScriptableInvalidate(NPObject* aObject);
 
@@ -186,19 +221,31 @@ private:
                       uint32_t* aCount);
 
   static bool
   ScriptableConstruct(NPObject* aObject,
                       const NPVariant* aArgs,
                       uint32_t aArgCount,
                       NPVariant* aResult);
 
+  NPObject*
+  CreateProxyObject();
+
+  // ResurrectProxyObject is only used with Proxy actors. It is called when the
+  // parent process uses an actor whose NPObject was deleted by the child
+  // process.
+  bool ResurrectProxyObject();
+
 private:
   PluginInstanceChild* mInstance;
   NPObject* mObject;
+  bool mInvalidated;
+  int mProtectCount;
+
+  ScriptableObjectType mType;
 
   static const NPClass sNPClass;
 };
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
 #endif /* dom_plugins_PluginScriptableObjectChild_h */
--- a/dom/plugins/PluginScriptableObjectParent.cpp
+++ b/dom/plugins/PluginScriptableObjectParent.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=4 ts=4 et :
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -32,85 +32,23 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "PluginScriptableObjectParent.h"
-#include "PluginInstanceParent.h"
-#include "PluginModuleParent.h"
-
-#include "npapi.h"
-#include "nsDebug.h"
+#include "PluginScriptableObjectUtils.h"
 
 using namespace mozilla::plugins;
-
 using mozilla::ipc::NPRemoteIdentifier;
 
 namespace {
 
-inline PluginInstanceParent*
-GetInstance(NPObject* aObject)
-{
-  NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(),
-               "Bad class!");
-
-  ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
-    NS_WARNING("Calling method on an invalidated object!");
-    return nsnull;
-  }
-  return object->parent->GetInstance();
-}
-
-inline const NPNetscapeFuncs*
-GetNetscapeFuncs(PluginInstanceParent* aInstance)
-{
-  PluginModuleParent* module = aInstance->Module();
-  if (!module) {
-    NS_WARNING("Null module?!");
-    return nsnull;
-  }
-  return module->GetNetscapeFuncs();
-}
-
-inline const NPNetscapeFuncs*
-GetNetscapeFuncs(NPObject* aObject)
-{
-  NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(),
-               "Bad class!");
-
-  PluginInstanceParent* instance = GetInstance(aObject);
-  if (!instance) {
-    return nsnull;
-  }
-
-  return GetNetscapeFuncs(instance);
-}
-
-inline NPObject*
-NPObjectFromVariant(const Variant& aRemoteVariant) {
-  NS_ASSERTION(aRemoteVariant.type() ==
-               Variant::TPPluginScriptableObjectParent,
-               "Wrong variant type!");
-  PluginScriptableObjectParent* actor =
-    const_cast<PluginScriptableObjectParent*>(
-      reinterpret_cast<const PluginScriptableObjectParent*>(
-        aRemoteVariant.get_PPluginScriptableObjectParent()));
-  return actor->GetObject();
-}
-
-inline NPObject*
-NPObjectFromVariant(const NPVariant& aVariant) {
-  NS_ASSERTION(NPVARIANT_IS_OBJECT(aVariant), "Wrong variant type!");
-  return NPVARIANT_TO_OBJECT(aVariant);
-}
-
 inline void
 ReleaseVariant(NPVariant& aVariant,
                PluginInstanceParent* aInstance)
 {
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
   if (npn) {
     npn->releasevariantvalue(&aVariant);
   }
@@ -137,220 +75,104 @@ EnsureValidIdentifier(NPObject* aObject,
   if (!instance) {
     NS_WARNING("Huh?!");
     return false;
   }
 
   return EnsureValidIdentifier(instance, aIdentifier);
 }
 
-bool
-ConvertToVariant(const Variant& aRemoteVariant,
-                 NPVariant& aVariant,
-                 PluginInstanceParent* aInstance)
-{
-  switch (aRemoteVariant.type()) {
-    case Variant::Tvoid_t: {
-      VOID_TO_NPVARIANT(aVariant);
-      break;
-    }
-
-    case Variant::Tnull_t: {
-      NULL_TO_NPVARIANT(aVariant);
-      break;
-    }
-
-    case Variant::Tbool: {
-      BOOLEAN_TO_NPVARIANT(aRemoteVariant.get_bool(), aVariant);
-      break;
-    }
-
-    case Variant::Tint: {
-      INT32_TO_NPVARIANT(aRemoteVariant.get_int(), aVariant);
-      break;
-    }
-
-    case Variant::Tdouble: {
-      DOUBLE_TO_NPVARIANT(aRemoteVariant.get_double(), aVariant);
-      break;
-    }
-
-    case Variant::TnsCString: {
-      const nsCString& string = aRemoteVariant.get_nsCString();
-      NPUTF8* buffer = reinterpret_cast<NPUTF8*>(strdup(string.get()));
-      if (!buffer) {
-        NS_ERROR("Out of memory!");
-        return false;
-      }
-      STRINGN_TO_NPVARIANT(buffer, string.Length(), aVariant);
-      break;
-    }
-
-    case Variant::TPPluginScriptableObjectParent: {
-      NPObject* object = NPObjectFromVariant(aRemoteVariant);
-      if (!object) {
-        NS_ERROR("Er, this shouldn't fail!");
-        return false;
-      }
-
-      const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
-      if (!npn) {
-        NS_ERROR("Null netscape funcs!");
-        return false;
-      }
-      npn->retainobject(object);
-      OBJECT_TO_NPVARIANT(object, aVariant);
-      break;
-    }
-
-    default:
-      NS_NOTREACHED("Shouldn't get here!");
-      return false;
-  }
-
-  return true;
-}
-
-bool
-ConvertToRemoteVariant(const NPVariant& aVariant,
-                       Variant& aRemoteVariant,
-                       PluginInstanceParent* aInstance)
-{
-  if (NPVARIANT_IS_VOID(aVariant)) {
-    aRemoteVariant = mozilla::void_t();
-  }
-  else if (NPVARIANT_IS_NULL(aVariant)) {
-    aRemoteVariant = mozilla::null_t();
-  }
-  else if (NPVARIANT_IS_BOOLEAN(aVariant)) {
-    aRemoteVariant = NPVARIANT_TO_BOOLEAN(aVariant);
-  }
-  else if (NPVARIANT_IS_INT32(aVariant)) {
-    aRemoteVariant = NPVARIANT_TO_INT32(aVariant);
-  }
-  else if (NPVARIANT_IS_DOUBLE(aVariant)) {
-    aRemoteVariant = NPVARIANT_TO_DOUBLE(aVariant);
-  }
-  else if (NPVARIANT_IS_STRING(aVariant)) {
-    NPString str = NPVARIANT_TO_STRING(aVariant);
-    nsCString string(str.UTF8Characters, str.UTF8Length);
-    aRemoteVariant = string;
-  }
-  else if (NPVARIANT_IS_OBJECT(aVariant)) {
-    NPObject* object = NPVARIANT_TO_OBJECT(aVariant);
-    PluginScriptableObjectParent* actor = aInstance->GetActorForNPObject(object);
-    if (!actor) {
-      NS_ERROR("Null actor!");
-      return false;
-    }
-    aRemoteVariant = actor;
-  }
-  else {
-    NS_NOTREACHED("Shouldn't get here!");
-    return false;
-  }
-
-  return true;
-}
-
 } // anonymous namespace
 
 // static
 NPObject*
 PluginScriptableObjectParent::ScriptableAllocate(NPP aInstance,
                                                  NPClass* aClass)
 {
-  NS_ASSERTION(aClass == PluginScriptableObjectParent::GetClass(),
-               "Huh?! Wrong class!");
-
-  PluginInstanceParent* instance = PluginModuleParent::InstCast(aInstance);
-  NS_ASSERTION(instance, "This should never be null!");
-
-  const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
-  if (!npn) {
-    NS_WARNING("Can't allocate!");
+  if (aClass != GetClass()) {
+    NS_ERROR("Huh?! Wrong class!");
     return nsnull;
   }
 
   return new ParentNPObject();
 }
 
 // static
 void
 PluginScriptableObjectParent::ScriptableInvalidate(NPObject* aObject)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     // This can happen more than once, and is just fine.
     return;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-
-  object->parent = NULL;
-
-  PluginInstanceParent* instance = actor->GetInstance();
-  NS_WARN_IF_FALSE(instance, "No instance?!");
+  object->invalidated = true;
 
-  if (!actor->CallInvalidate()) {
-    NS_WARNING("Failed to send message!");
-  }
-
-  if (instance &&
-      !PPluginScriptableObjectParent::Call__delete__(actor)) {
-    NS_WARNING("Failed to send message!");
+  // |object->parent| may be null already if the instance has gone away.
+  if (object->parent && !object->parent->CallInvalidate()) {
+    NS_ERROR("Failed to send message!");
   }
 }
 
 // static
 void
 PluginScriptableObjectParent::ScriptableDeallocate(NPObject* aObject)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (object->parent) {
+  if (!object->invalidated) {
     ScriptableInvalidate(aObject);
   }
+  NS_ASSERTION(object->invalidated, "Should have invalidated already!");
 
-  NS_ASSERTION(!object->parent, "Should be invalidated!");
+  PluginScriptableObjectParent* actor = object->parent;
+  if (actor) {
+    NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+    actor->DropNPObject();
+  }
 
   delete object;
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableHasMethod(NPObject* aObject,
                                                   NPIdentifier aName)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   if (!EnsureValidIdentifier(aObject, aName)) {
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
+    return false;
+  }
+
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
   if (!actor->CallHasMethod((NPRemoteIdentifier)aName, &result)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
 
   return result;
@@ -359,52 +181,48 @@ PluginScriptableObjectParent::Scriptable
 // static
 bool
 PluginScriptableObjectParent::ScriptableInvoke(NPObject* aObject,
                                                NPIdentifier aName,
                                                const NPVariant* aArgs,
                                                uint32_t aArgCount,
                                                NPVariant* aResult)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   if (!EnsureValidIdentifier(aObject, aName)) {
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
-
-  nsAutoTArray<Variant, 10> args;
-  if (!args.SetLength(aArgCount)) {
-    NS_ERROR("Out of memory?!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
     return false;
   }
 
-  for (PRUint32 index = 0; index < aArgCount; index++) {
-    Variant& arg = args[index];
-    if (!ConvertToRemoteVariant(aArgs[index], arg, actor->GetInstance())) {
-      NS_WARNING("Failed to convert argument!");
-      return false;
-    }
+  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;
   if (!actor->CallInvoke((NPRemoteIdentifier)aName, args, &remoteResult,
-       &success)) {
+                         &success)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
 
   if (!success) {
     return false;
   }
 
@@ -417,42 +235,38 @@ PluginScriptableObjectParent::Scriptable
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableInvokeDefault(NPObject* aObject,
                                                       const NPVariant* aArgs,
                                                       uint32_t aArgCount,
                                                       NPVariant* aResult)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
-
-  nsAutoTArray<Variant, 10> args;
-  if (!args.SetLength(aArgCount)) {
-    NS_ERROR("Out of memory?!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
     return false;
   }
 
-  for (PRUint32 index = 0; index < aArgCount; index++) {
-    Variant& arg = args[index];
-    if (!ConvertToRemoteVariant(aArgs[index], arg, actor->GetInstance())) {
-      NS_WARNING("Failed to convert argument!");
-      return false;
-    }
+  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;
   if (!actor->CallInvokeDefault(args, &remoteResult, &success)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
@@ -468,66 +282,74 @@ PluginScriptableObjectParent::Scriptable
   return true;
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableHasProperty(NPObject* aObject,
                                                     NPIdentifier aName)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   if (!EnsureValidIdentifier(aObject, aName)) {
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
+    return false;
+  }
+
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
   if (!actor->CallHasProperty((NPRemoteIdentifier)aName, &result)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
 
   return result;
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableGetProperty(NPObject* aObject,
                                                     NPIdentifier aName,
                                                     NPVariant* aResult)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   if (!EnsureValidIdentifier(aObject, aName)) {
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
+    return false;
+  }
+
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   Variant result;
   bool success;
   if (!actor->CallGetProperty((NPRemoteIdentifier)aName, &result, &success)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
 
@@ -544,36 +366,40 @@ PluginScriptableObjectParent::Scriptable
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableSetProperty(NPObject* aObject,
                                                     NPIdentifier aName,
                                                     const NPVariant* aValue)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   if (!EnsureValidIdentifier(aObject, aName)) {
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
+    return false;
+  }
 
-  Variant value;
-  if (!ConvertToRemoteVariant(*aValue, value, actor->GetInstance())) {
+  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;
   if (!actor->CallSetProperty((NPRemoteIdentifier)aName, value, &success)) {
     NS_WARNING("Failed to send message!");
     return false;
@@ -582,62 +408,70 @@ PluginScriptableObjectParent::Scriptable
   return success;
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableRemoveProperty(NPObject* aObject,
                                                        NPIdentifier aName)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   if (!EnsureValidIdentifier(aObject, aName)) {
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
+    return false;
+  }
+
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool success;
   if (!actor->CallRemoveProperty((NPRemoteIdentifier)aName, &success)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
 
   return success;
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableEnumerate(NPObject* aObject,
                                                   NPIdentifier** aIdentifiers,
                                                   uint32_t* aCount)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
+    return false;
+  }
+
+  NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(aObject);
   if (!npn) {
     NS_ERROR("No netscape funcs!");
     return false;
   }
 
   nsAutoTArray<NPRemoteIdentifier, 10> identifiers;
@@ -675,42 +509,38 @@ PluginScriptableObjectParent::Scriptable
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableConstruct(NPObject* aObject,
                                                   const NPVariant* aArgs,
                                                   uint32_t aArgCount,
                                                   NPVariant* aResult)
 {
-  if (aObject->_class != PluginScriptableObjectParent::GetClass()) {
+  if (aObject->_class != GetClass()) {
     NS_ERROR("Don't know what kind of object this is!");
     return false;
   }
 
   ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
-  if (!object->parent) {
+  if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
-  PluginScriptableObjectParent* actor = object->parent;
-  NS_ASSERTION(actor, "This shouldn't ever be null!");
-
-  nsAutoTArray<Variant, 10> args;
-  if (!args.SetLength(aArgCount)) {
-    NS_ERROR("Out of memory?!");
+  ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+  if (!actor) {
     return false;
   }
 
-  for (PRUint32 index = 0; index < aArgCount; index++) {
-    Variant& arg = args[index];
-    if (!ConvertToRemoteVariant(aArgs[index], arg, actor->GetInstance())) {
-      NS_WARNING("Failed to convert argument!");
-      return false;
-    }
+  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;
   if (!actor->CallConstruct(args, &remoteResult, &success)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
@@ -737,87 +567,191 @@ const NPClass PluginScriptableObjectPare
   PluginScriptableObjectParent::ScriptableHasProperty,
   PluginScriptableObjectParent::ScriptableGetProperty,
   PluginScriptableObjectParent::ScriptableSetProperty,
   PluginScriptableObjectParent::ScriptableRemoveProperty,
   PluginScriptableObjectParent::ScriptableEnumerate,
   PluginScriptableObjectParent::ScriptableConstruct
 };
 
-PluginScriptableObjectParent::PluginScriptableObjectParent()
+PluginScriptableObjectParent::PluginScriptableObjectParent(
+                                                     ScriptableObjectType aType)
 : mInstance(nsnull),
-  mObject(nsnull)
+  mObject(nsnull),
+  mProtectCount(0),
+  mType(aType)
 {
 }
 
 PluginScriptableObjectParent::~PluginScriptableObjectParent()
 {
   if (mObject) {
-    if (GetClass() == mObject->_class) {
-      static_cast<ParentNPObject*>(mObject)->parent = NULL;
+    if (mObject->_class == GetClass()) {
+      NS_ASSERTION(mType == Proxy, "Wrong type!");
+      static_cast<ParentNPObject*>(mObject)->parent = nsnull;
     }
     else {
-      mInstance->GetNPNIface()->releaseobject(mObject);
+      NS_ASSERTION(mType == LocalObject, "Wrong type!");
+      GetInstance()->GetNPNIface()->releaseobject(mObject);
     }
   }
 }
 
 void
-PluginScriptableObjectParent::Initialize(PluginInstanceParent* aInstance,
-                                         NPObject* aObject)
+PluginScriptableObjectParent::InitializeProxy()
 {
-  NS_ASSERTION(aInstance && aObject, "Null pointers!");
+  NS_ASSERTION(mType == Proxy, "Bad type!");
+  NS_ASSERTION(!mObject, "Calling Initialize more than once!");
+
+  mInstance = static_cast<PluginInstanceParent*>(Manager());
+  NS_ASSERTION(mInstance, "Null manager?!");
+
+  NPObject* object = CreateProxyObject();
+  NS_ASSERTION(object, "Failed to create object!");
+
+  if (!mInstance->RegisterNPObjectForActor(object, this)) {
+    NS_ERROR("Out of memory?");
+  }
+
+  mObject = object;
+}
+
+void
+PluginScriptableObjectParent::InitializeLocal(NPObject* aObject)
+{
+  NS_ASSERTION(mType == LocalObject, "Bad type!");
   NS_ASSERTION(!(mInstance && mObject), "Calling Initialize more than once!");
 
-  if (aObject->_class == GetClass()) {
-    ParentNPObject* object = static_cast<ParentNPObject*>(aObject);
+  mInstance = static_cast<PluginInstanceParent*>(Manager());
+  NS_ASSERTION(mInstance, "Null manager?!");
 
-    NS_ASSERTION(!object->parent, "Bad object!");
-    object->parent = const_cast<PluginScriptableObjectParent*>(this);
+  mInstance->GetNPNIface()->retainobject(aObject);
 
-    // 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.
-    NS_ASSERTION(aObject->referenceCount == 1, "Some kind of live object!");
-    object->referenceCount = 0;
-    NS_LOG_RELEASE(aObject, 0, "BrowserNPObject");
-  }
-  else {
-    aInstance->GetNPNIface()->retainobject(aObject);
+  NS_ASSERTION(!mProtectCount, "Should be zero!");
+  mProtectCount++;
+
+  if (!mInstance->RegisterNPObjectForActor(aObject, this)) {
+    NS_ERROR("Out of memory?");
   }
 
-  mInstance = aInstance;
   mObject = aObject;
 }
 
+NPObject*
+PluginScriptableObjectParent::CreateProxyObject()
+{
+  NS_ASSERTION(mInstance, "Must have an instance!");
+  NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+  const NPNetscapeFuncs* npn = GetNetscapeFuncs(mInstance);
+
+  NPObject* npobject = npn->createobject(mInstance->GetNPP(),
+                                         const_cast<NPClass*>(GetClass()));
+  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!");
+
+  ParentNPObject* object = static_cast<ParentNPObject*>(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, "BrowserNPObject");
+
+  object->parent = const_cast<PluginScriptableObjectParent*>(this);
+  return object;
+}
+
 bool
-PluginScriptableObjectParent::AnswerInvalidate()
+PluginScriptableObjectParent::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!");
+
+  InitializeProxy();
+  NS_ASSERTION(mObject, "Initialize failed!");
+
+  if (!CallProtect()) {
+    NS_WARNING("Failed to send message!");
+    return false;
+  }
+
+  return true;
+}
+
+NPObject*
+PluginScriptableObjectParent::GetObject(bool aCanResurrect)
 {
-  if (mObject) {
-    NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
-    const NPNetscapeFuncs* npn = GetNetscapeFuncs(GetInstance());
-    if (npn) {
-      npn->releaseobject(mObject);
+  if (!mObject && aCanResurrect && !ResurrectProxyObject()) {
+    NS_ERROR("Null object!");
+    return nsnull;
+  }
+  return mObject;
+}
+
+void
+PluginScriptableObjectParent::Protect()
+{
+  NS_ASSERTION(mObject, "No object!");
+  NS_ASSERTION(mProtectCount >= 0, "Negative protect count?!");
+
+  if (mType == LocalObject) {
+    ++mProtectCount;
+  }
+}
+
+void
+PluginScriptableObjectParent::Unprotect()
+{
+  NS_ASSERTION(mObject, "No object!");
+  NS_ASSERTION(mProtectCount >= 0, "Negative protect count?!");
+
+  if (mType == LocalObject) {
+    if (--mProtectCount == 0) {
+      PluginScriptableObjectParent::Call__delete__(this);
     }
-    mObject = nsnull;
   }
-  return true;
+}
+
+void
+PluginScriptableObjectParent::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.
+  PluginInstanceParent* instance = GetInstance();
+  NS_ASSERTION(instance, "Must have an instance!");
+
+  instance->UnregisterNPObject(mObject);
+  mObject = nsnull;
+
+  if (!CallUnprotect()) {
+    NS_WARNING("Failed to send message!");
+  }
 }
 
 bool
 PluginScriptableObjectParent::AnswerHasMethod(const NPRemoteIdentifier& aId,
                                               bool* aHasMethod)
 {
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aHasMethod = false;
     return true;
   }
 
@@ -847,16 +781,17 @@ PluginScriptableObjectParent::AnswerInvo
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -909,17 +844,17 @@ PluginScriptableObjectParent::AnswerInvo
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
   success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
 
-  ReleaseVariant(result, instance);
+  DeferNPVariantLastRelease(npn, &result);
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   *aResult = convertedResult;
@@ -935,16 +870,17 @@ PluginScriptableObjectParent::AnswerInvo
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -991,17 +927,17 @@ PluginScriptableObjectParent::AnswerInvo
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
   success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
 
-  ReleaseVariant(result, instance);
+  DeferNPVariantLastRelease(npn, &result);
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   *aResult = convertedResult;
@@ -1015,16 +951,17 @@ PluginScriptableObjectParent::AnswerHasP
 {
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aHasProperty = false;
     return true;
   }
 
@@ -1054,16 +991,17 @@ PluginScriptableObjectParent::AnswerGetP
   if (!mObject) {
     NS_WARNING("Calling AnswerGetProperty 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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -1088,17 +1026,17 @@ PluginScriptableObjectParent::AnswerGetP
                         &result)) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant converted;
   if ((*aSuccess = ConvertToRemoteVariant(result, converted, instance))) {
-    ReleaseVariant(result, instance);
+    DeferNPVariantLastRelease(npn, &result);
     *aResult = converted;
   }
   else {
     *aResult = void_t();
   }
 
   return true;
 }
@@ -1110,16 +1048,17 @@ PluginScriptableObjectParent::AnswerSetP
 {
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aSuccess = false;
     return true;
   }
 
@@ -1155,16 +1094,17 @@ PluginScriptableObjectParent::AnswerRemo
 {
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aSuccess = false;
     return true;
   }
 
@@ -1192,16 +1132,17 @@ PluginScriptableObjectParent::AnswerEnum
 {
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aSuccess = false;
     return true;
   }
 
@@ -1248,16 +1189,17 @@ PluginScriptableObjectParent::AnswerCons
   if (!mObject) {
     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!");
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -1303,30 +1245,50 @@ PluginScriptableObjectParent::AnswerCons
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
   success = ConvertToRemoteVariant(result, convertedResult, instance);
 
-  ReleaseVariant(result, instance);
+  DeferNPVariantLastRelease(npn, &result);
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   *aSuccess = true;
   *aResult = convertedResult;
   return true;
 }
 
 bool
+PluginScriptableObjectParent::AnswerProtect()
+{
+  NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+  NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+  Protect();
+  return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerUnprotect()
+{
+  NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+  NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+  Unprotect();
+  return true;
+}
+
+bool
 PluginScriptableObjectParent::AnswerNPN_Evaluate(const nsCString& aScript,
                                                  Variant* aResult,
                                                  bool* aSuccess)
 {
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
@@ -1350,17 +1312,17 @@ PluginScriptableObjectParent::AnswerNPN_
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant convertedResult;
   success = ConvertToRemoteVariant(result, convertedResult, instance);
 
-  ReleaseVariant(result, instance);
+  DeferNPVariantLastRelease(npn, &result);
 
   if (!success) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   *aSuccess = true;
--- a/dom/plugins/PluginScriptableObjectParent.h
+++ b/dom/plugins/PluginScriptableObjectParent.h
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=4 ts=4 et :
+ * vim: sw=2 ts=2 et :
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -48,31 +48,37 @@ namespace mozilla {
 namespace plugins {
 
 class PluginInstanceParent;
 class PluginScriptableObjectParent;
 
 struct ParentNPObject : NPObject
 {
   ParentNPObject()
-    : parent(NULL) { }
+    : NPObject(), parent(NULL), invalidated(false) { }
 
+  // |parent| is always valid as long as the actor is alive. Once the actor is
+  // destroyed this will be set to null.
   PluginScriptableObjectParent* parent;
+  bool invalidated;
 };
 
 class PluginScriptableObjectParent : public PPluginScriptableObjectParent
 {
   friend class PluginInstanceParent;
 
 public:
-  PluginScriptableObjectParent();
+  PluginScriptableObjectParent(ScriptableObjectType aType);
   virtual ~PluginScriptableObjectParent();
 
-  virtual bool
-  AnswerInvalidate();
+  void
+  InitializeProxy();
+
+  void
+  InitializeLocal(NPObject* aObject);
 
   virtual bool
   AnswerHasMethod(const NPRemoteIdentifier& aId,
                   bool* aHasMethod);
 
   virtual bool
   AnswerInvoke(const NPRemoteIdentifier& aId,
                const nsTArray<Variant>& aArgs,
@@ -111,36 +117,59 @@ public:
                   Variant* aResult,
                   bool* aSuccess);
 
   virtual bool
   AnswerNPN_Evaluate(const nsCString& aScript,
                      Variant* aResult,
                      bool* aSuccess);
 
-  void
-  Initialize(PluginInstanceParent* aInstance,
-             NPObject* aObject);
+  virtual bool
+  AnswerProtect();
+
+  virtual bool
+  AnswerUnprotect();
 
   static const NPClass*
   GetClass()
   {
     return &sNPClass;
   }
 
   PluginInstanceParent*
-  GetInstance()
+  GetInstance() const
   {
     return mInstance;
   }
 
   NPObject*
-  GetObject()
-  {
-    return mObject;
+  GetObject(bool aCanResurrect);
+
+  // Protect only affects LocalObject actors. It is called by the
+  // ProtectedVariant/Actor helper classes before the actor is used as an
+  // argument to an IPC call and when the child process resurrects a
+  // proxy object to the NPObject associated with this actor.
+  void Protect();
+
+  // Unprotect only affects LocalObject actors. It is called by the
+  // ProtectedVariant/Actor helper classes after the actor is used as an
+  // argument to an IPC call and when the child process is no longer using this
+  // actor.
+  void Unprotect();
+
+  // DropNPObject is only used for Proxy actors and is called when the parent
+  // process is no longer using the NPObject associated with this actor. The
+  // child process may subsequently use this actor again in which case a new
+  // NPObject will be created and associated with this actor (see
+  // ResurrectProxyObject).
+  void DropNPObject();
+
+  ScriptableObjectType
+  Type() const {
+    return mType;
   }
 
 private:
   static NPObject*
   ScriptableAllocate(NPP aInstance,
                      NPClass* aClass);
 
   static void
@@ -190,22 +219,33 @@ private:
                       uint32_t* aCount);
 
   static bool
   ScriptableConstruct(NPObject* aObject,
                       const NPVariant* aArgs,
                       uint32_t aArgCount,
                       NPVariant* aResult);
 
+  NPObject*
+  CreateProxyObject();
+
+  // ResurrectProxyObject is only used with Proxy actors. It is called when the
+  // child process uses an actor whose NPObject was deleted by the parent
+  // process.
+  bool ResurrectProxyObject();
+
 private:
   PluginInstanceParent* mInstance;
 
   // This may be a ParentNPObject or some other kind depending on who created
   // it. Have to check its class to find out.
   NPObject* mObject;
+  int mProtectCount;
+
+  ScriptableObjectType mType;
 
   static const NPClass sNPClass;
 };
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
 #endif /* dom_plugins_PluginScriptableObjectParent_h */
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginScriptableObjectUtils.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "PluginScriptableObjectUtils.h"
+
+using namespace mozilla::plugins;
+
+namespace {
+
+template<class InstanceType>
+class VariantTraits;
+
+template<>
+class VariantTraits<PluginInstanceParent>
+{
+public:
+  typedef PluginScriptableObjectParent ScriptableObjectType;
+};
+
+template<>
+class VariantTraits<PluginInstanceChild>
+{
+public:
+  typedef PluginScriptableObjectChild ScriptableObjectType;
+};
+
+} /* anonymous namespace */
+
+bool
+mozilla::plugins::ConvertToVariant(const Variant& aRemoteVariant,
+                                   NPVariant& aVariant,
+                                   PluginInstanceParent* aInstance)
+{
+  switch (aRemoteVariant.type()) {
+    case Variant::Tvoid_t: {
+      VOID_TO_NPVARIANT(aVariant);
+      break;
+    }
+
+    case Variant::Tnull_t: {
+      NULL_TO_NPVARIANT(aVariant);
+      break;
+    }
+
+    case Variant::Tbool: {
+      BOOLEAN_TO_NPVARIANT(aRemoteVariant.get_bool(), aVariant);
+      break;
+    }
+
+    case Variant::Tint: {
+      INT32_TO_NPVARIANT(aRemoteVariant.get_int(), aVariant);
+      break;
+    }
+
+    case Variant::Tdouble: {
+      DOUBLE_TO_NPVARIANT(aRemoteVariant.get_double(), aVariant);
+      break;
+    }
+
+    case Variant::TnsCString: {
+      const nsCString& string = aRemoteVariant.get_nsCString();
+      NPUTF8* buffer = reinterpret_cast<NPUTF8*>(strdup(string.get()));
+      if (!buffer) {
+        NS_ERROR("Out of memory!");
+        return false;
+      }
+      STRINGN_TO_NPVARIANT(buffer, string.Length(), aVariant);
+      break;
+    }
+
+    case Variant::TPPluginScriptableObjectParent: {
+      NS_ASSERTION(aInstance, "Must have an instance!");
+      NPObject* object = NPObjectFromVariant(aRemoteVariant);
+      if (!object) {
+        NS_ERROR("Er, this shouldn't fail!");
+        return false;
+      }
+
+      const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
+      if (!npn) {
+        NS_ERROR("Null netscape funcs!");
+        return false;
+      }
+
+      npn->retainobject(object);
+      OBJECT_TO_NPVARIANT(object, aVariant);
+      break;
+    }
+
+    case Variant::TPPluginScriptableObjectChild: {
+      NS_ASSERTION(!aInstance, "No instance should be given!");
+      NS_ASSERTION(PluginModuleChild::current(),
+                   "Should be running on child only!");
+
+      NPObject* object = NPObjectFromVariant(aRemoteVariant);
+      NS_ASSERTION(object, "Null object?!");
+
+      PluginModuleChild::sBrowserFuncs.retainobject(object);
+      OBJECT_TO_NPVARIANT(object, aVariant);
+      break;
+    }
+
+    default:
+      NS_NOTREACHED("Shouldn't get here!");
+      return false;
+  }
+
+  return true;
+}
+
+template <class InstanceType>
+bool
+mozilla::plugins::ConvertToRemoteVariant(const NPVariant& aVariant,
+                                         Variant& aRemoteVariant,
+                                         InstanceType* aInstance,
+                                         bool aProtectActors)
+{
+  if (NPVARIANT_IS_VOID(aVariant)) {
+    aRemoteVariant = mozilla::void_t();
+  }
+  else if (NPVARIANT_IS_NULL(aVariant)) {
+    aRemoteVariant = mozilla::null_t();
+  }
+  else if (NPVARIANT_IS_BOOLEAN(aVariant)) {
+    aRemoteVariant = NPVARIANT_TO_BOOLEAN(aVariant);
+  }
+  else if (NPVARIANT_IS_INT32(aVariant)) {
+    aRemoteVariant = NPVARIANT_TO_INT32(aVariant);
+  }
+  else if (NPVARIANT_IS_DOUBLE(aVariant)) {
+    aRemoteVariant = NPVARIANT_TO_DOUBLE(aVariant);
+  }
+  else if (NPVARIANT_IS_STRING(aVariant)) {
+    NPString str = NPVARIANT_TO_STRING(aVariant);
+    nsCString string(str.UTF8Characters, str.UTF8Length);
+    aRemoteVariant = string;
+  }
+  else if (NPVARIANT_IS_OBJECT(aVariant)) {
+    NPObject* object = NPVARIANT_TO_OBJECT(aVariant);
+
+    typename VariantTraits<InstanceType>::ScriptableObjectType* actor =
+      aInstance->GetActorForNPObject(object);
+
+    if (!actor) {
+      NS_ERROR("Null actor!");
+      return false;
+    }
+
+    if (aProtectActors) {
+      actor->Protect();
+    }
+
+    aRemoteVariant = actor;
+  }
+  else {
+    NS_NOTREACHED("Shouldn't get here!");
+    return false;
+  }
+
+  return true;
+}
+
+namespace {
+
+// This function only exists to get both flavors of the ConvertToRemoteVariant
+// function instantiated and should never be called.
+
+void
+XXXNeverCalled()
+{
+  NS_NOTREACHED("This should never be called!");
+  PluginInstanceParent* parent = nsnull;
+  PluginInstanceChild* child = nsnull;
+  NPVariant variant;
+  VOID_TO_NPVARIANT(variant);
+  Variant remoteVariant;
+  ConvertToRemoteVariant(variant, remoteVariant, parent, false);
+  ConvertToRemoteVariant(variant, remoteVariant, child, false);
+}
+
+} /* anonymous namespace */
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginScriptableObjectUtils.h
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Plugin App.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef dom_plugins_PluginScriptableObjectUtils_h
+#define dom_plugins_PluginScriptableObjectUtils_h
+
+#include "PluginModuleParent.h"
+#include "PluginModuleChild.h"
+#include "PluginInstanceParent.h"
+#include "PluginInstanceChild.h"
+#include "PluginScriptableObjectParent.h"
+#include "PluginScriptableObjectChild.h"
+
+#include "npapi.h"
+#include "npfunctions.h"
+#include "npruntime.h"
+
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace plugins {
+
+inline PluginInstanceParent*
+GetInstance(NPObject* aObject)
+{
+  NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(),
+               "Bad class!");
+
+  ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+  if (object->invalidated) {
+    NS_WARNING("Calling method on an invalidated object!");
+    return nsnull;
+  }
+  if (!object->parent) {
+    return nsnull;
+  }
+  return object->parent->GetInstance();
+}
+
+inline NPObject*
+NPObjectFromVariant(const Variant& aRemoteVariant)
+{
+  switch (aRemoteVariant.type()) {
+    case Variant::TPPluginScriptableObjectParent: {
+      PluginScriptableObjectParent* actor =
+        const_cast<PluginScriptableObjectParent*>(
+          reinterpret_cast<const PluginScriptableObjectParent*>(
+            aRemoteVariant.get_PPluginScriptableObjectParent()));
+      return actor->GetObject(true);
+    }
+
+    case Variant::TPPluginScriptableObjectChild: {
+      PluginScriptableObjectChild* actor =
+        const_cast<PluginScriptableObjectChild*>(
+          reinterpret_cast<const PluginScriptableObjectChild*>(
+            aRemoteVariant.get_PPluginScriptableObjectChild()));
+      return actor->GetObject(true);
+    }
+
+    default:
+      NS_NOTREACHED("Shouldn't get here!");
+      return nsnull;
+  }
+}
+
+inline NPObject*
+NPObjectFromVariant(const NPVariant& aVariant)
+{
+  NS_ASSERTION(NPVARIANT_IS_OBJECT(aVariant), "Wrong variant type!");
+  return NPVARIANT_TO_OBJECT(aVariant);
+}
+
+inline const NPNetscapeFuncs*
+GetNetscapeFuncs(PluginInstanceParent* aInstance)
+{
+  PluginModuleParent* module = aInstance->Module();
+  if (!module) {
+    NS_WARNING("Null module?!");
+    return nsnull;
+  }
+  return module->GetNetscapeFuncs();
+}
+
+inline const NPNetscapeFuncs*
+GetNetscapeFuncs(NPObject* aObject)
+{
+  NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(),
+               "Bad class!");
+
+  PluginInstanceParent* instance = GetInstance(aObject);
+  if (!instance) {
+    return nsnull;
+  }
+
+  return GetNetscapeFuncs(instance);
+}
+
+inline void
+ReleaseRemoteVariant(Variant& aVariant)
+{
+  switch (aVariant.type()) {
+    case Variant::TPPluginScriptableObjectParent: {
+      PluginScriptableObjectParent* actor =
+        const_cast<PluginScriptableObjectParent*>(
+          reinterpret_cast<const PluginScriptableObjectParent*>(
+            aVariant.get_PPluginScriptableObjectParent()));
+      actor->Unprotect();
+      break;
+    }
+
+    case Variant::TPPluginScriptableObjectChild: {
+      NS_ASSERTION(PluginModuleChild::current(),
+                   "Should only be running in the child!");
+      PluginScriptableObjectChild* actor =
+        const_cast<PluginScriptableObjectChild*>(
+          reinterpret_cast<const PluginScriptableObjectChild*>(
+            aVariant.get_PPluginScriptableObjectChild()));
+      actor->Unprotect();
+      break;
+    }
+
+  default:
+    break; // Intentional fall-through for other variant types.
+  }
+
+  aVariant = mozilla::void_t();
+}
+
+bool
+ConvertToVariant(const Variant& aRemoteVariant,
+                 NPVariant& aVariant,
+                 PluginInstanceParent* aInstance = nsnull);
+
+template <class InstanceType>
+bool
+ConvertToRemoteVariant(const NPVariant& aVariant,
+                       Variant& aRemoteVariant,
+                       InstanceType* aInstance,
+                       bool aProtectActors = false);
+
+class ProtectedVariant
+{
+public:
+  ProtectedVariant(const NPVariant& aVariant,
+                   PluginInstanceParent* aInstance)
+  {
+    mOk = ConvertToRemoteVariant(aVariant, mVariant, aInstance, true);
+  }
+
+  ProtectedVariant(const NPVariant& aVariant,
+                   PluginInstanceChild* aInstance)
+  {
+    mOk = ConvertToRemoteVariant(aVariant, mVariant, aInstance, true);
+  }
+
+  ~ProtectedVariant() {
+    ReleaseRemoteVariant(mVariant);
+  }
+
+  PRBool IsOk() {
+    return mOk;
+  }
+
+  operator const Variant&() {
+    return mVariant;
+  }
+
+private:
+  Variant mVariant;
+  bool mOk;
+};
+
+class ProtectedVariantArray
+{
+public:
+  ProtectedVariantArray(const NPVariant* aArgs,
+                        PRUint32 aCount,
+                        PluginInstanceParent* aInstance)
+  {
+    for (PRUint32 index = 0; index < aCount; index++) {
+      Variant* remoteVariant = mArray.AppendElement();
+      if (!(remoteVariant && 
+            ConvertToRemoteVariant(aArgs[index], *remoteVariant, aInstance,
+                                   true))) {
+        break;
+      }
+    }
+    mOk = mArray.Length() == aCount;
+  }
+
+  ProtectedVariantArray(const NPVariant* aArgs,
+                        PRUint32 aCount,
+                        PluginInstanceChild* aInstance)
+  {
+    for (PRUint32 index = 0; index < aCount; index++) {
+      Variant* remoteVariant = mArray.AppendElement();
+      if (!(remoteVariant && 
+            ConvertToRemoteVariant(aArgs[index], *remoteVariant, aInstance,
+                                   true))) {
+        break;
+      }
+    }
+    mOk = mArray.Length() == aCount;
+  }
+  ~ProtectedVariantArray()
+  {
+    PRUint32 count = mArray.Length();
+    for (PRUint32 index = 0; index < count; index++) {
+      ReleaseRemoteVariant(mArray[index]);
+    }
+  }
+
+  operator const nsTArray<Variant>&()
+  {
+    return mArray;
+  }
+
+  bool IsOk()
+  {
+    return mOk;
+  }
+
+private:
+  nsTArray<Variant> mArray;
+  bool mOk;
+};
+
+template<class ActorType>
+struct ProtectedActorTraits
+{
+  static bool Nullable();
+};
+
+template<class ActorType, class Traits=ProtectedActorTraits<ActorType> >
+class ProtectedActor
+{
+public:
+  ProtectedActor(ActorType* aActor) : mActor(aActor)
+  {
+    if (!Traits::Nullable()) {
+      NS_ASSERTION(mActor, "This should never be null!");
+    }
+  }
+
+  ~ProtectedActor()
+  {
+    if (Traits::Nullable() && !mActor)
+      return;
+    mActor->Unprotect();
+  }
+
+  ActorType* operator->()
+  {
+    return mActor;
+  }
+
+  operator bool()
+  {
+    return !!mActor;
+  }
+
+private:
+  ActorType* mActor;
+};
+
+template<>
+struct ProtectedActorTraits<PluginScriptableObjectParent>
+{
+  static bool Nullable() { return true; }
+};
+
+template<>
+struct ProtectedActorTraits<PluginScriptableObjectChild>
+{
+  static bool Nullable() { return false; }
+};
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#endif /* dom_plugins_PluginScriptableObjectUtils_h */
--- a/modules/plugin/base/src/nsNPAPIPlugin.cpp
+++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp
@@ -328,23 +328,23 @@ OOPPluginsEnabled()
   PRBool oopPluginsEnabled = PR_FALSE;
   prefs->GetBoolPref("dom.ipc.plugins.enabled", &oopPluginsEnabled);
 
   if (!oopPluginsEnabled) {
     return PR_FALSE;
   }
 
 #ifdef XP_WIN
-  OSVERSIONINFO osVerInfo = {0};
-  osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
-  GetVersionEx(&osVerInfo);
-  // Always disabled on 2K or less. (bug 536303)
-  if (osVerInfo.dwMajorVersion < 5 ||
-      (osVerInfo.dwMajorVersion == 5 && osVerInfo.dwMinorVersion == 0))
-    return PR_FALSE;
+  OSVERSIONINFO osVerInfo = {0};
+  osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
+  GetVersionEx(&osVerInfo);
+  // Always disabled on 2K or less. (bug 536303)
+  if (osVerInfo.dwMajorVersion < 5 ||
+      (osVerInfo.dwMajorVersion == 5 && osVerInfo.dwMinorVersion == 0))
+    return PR_FALSE;
 #endif
 
   return PR_TRUE;
 }
 
 #endif // MOZ_IPC
 
 inline PluginLibrary*