bug 525342: make IPDL dtors part of the destructed actor's protocol instead of the manager's. r=bsmedberg
authorChris Jones <jones.chris.g@gmail.com>
Thu, 03 Dec 2009 02:16:14 -0600
changeset 36138 5160e99545e97c8263d36774d87a70487cf7cf98
parent 36137 a006c48594102ef7eba67b126ed0fa44a3aa1c11
child 36139 b8b7a32cf66d061d17d390b67f6968da59e95564
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs525342
milestone1.9.3a1pre
bug 525342: make IPDL dtors part of the destructed actor's protocol instead of the manager's. r=bsmedberg
content/base/src/nsFrameLoader.cpp
content/canvas/public/DocumentRendererParent.h
content/canvas/src/DocumentRendererParent.cpp
dom/ipc/ContentProcessParent.cpp
dom/ipc/PContentProcess.ipdl
dom/ipc/PDocumentRenderer.ipdl
dom/ipc/PIFrameEmbedding.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/plugins/BrowserStreamChild.cpp
dom/plugins/BrowserStreamChild.h
dom/plugins/BrowserStreamParent.cpp
dom/plugins/BrowserStreamParent.h
dom/plugins/PBrowserStream.ipdl
dom/plugins/PPluginInstance.ipdl
dom/plugins/PPluginModule.ipdl
dom/plugins/PPluginScriptableObject.ipdl
dom/plugins/PPluginStream.ipdl
dom/plugins/PStreamNotify.ipdl
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceChild.h
dom/plugins/PluginInstanceParent.cpp
dom/plugins/PluginInstanceParent.h
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleChild.h
dom/plugins/PluginModuleParent.cpp
dom/plugins/PluginModuleParent.h
dom/plugins/PluginScriptableObjectChild.cpp
dom/plugins/PluginScriptableObjectParent.cpp
dom/plugins/PluginStreamChild.cpp
dom/plugins/PluginStreamChild.h
dom/plugins/PluginStreamParent.cpp
dom/plugins/PluginStreamParent.h
dom/plugins/StreamNotifyChild.h
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/builtin.py
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/test/cxx/PTestArrays.ipdl
ipc/ipdl/test/cxx/PTestArraysSub.ipdl
ipc/ipdl/test/cxx/PTestDesc.ipdl
ipc/ipdl/test/cxx/PTestDescSub.ipdl
ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
ipc/ipdl/test/cxx/PTestLatency.ipdl
ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
ipc/ipdl/test/cxx/PTestSanity.ipdl
ipc/ipdl/test/cxx/TestArrays.cpp
ipc/ipdl/test/cxx/TestArrays.h
ipc/ipdl/test/ipdl/error/DeleteRace.ipdl
ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl
ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl
ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl
ipc/ipdl/test/ipdl/error/redefState.ipdl
ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl
ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl
ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl
ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl
ipc/ipdl/test/ipdl/ok/Delete.ipdl
ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl
ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl
ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl
ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl
ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl
ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
ipc/testshell/PTestShell.ipdl
ipc/testshell/PTestShellCommand.ipdl
ipc/testshell/TestShellChild.cpp
ipc/testshell/TestShellChild.h
ipc/testshell/TestShellParent.cpp
ipc/testshell/TestShellParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/http/src/PHttpChannel.ipdl
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -993,17 +993,17 @@ nsFrameLoader::Destroy()
       doc->SetSubDocumentFor(mOwnerContent, nsnull);
     }
 
     mOwnerContent = nsnull;
   }
 #ifdef MOZ_IPC
   if (mChildProcess) {
     mChildProcess->SetOwnerElement(nsnull);
-    ContentProcessParent::GetSingleton()->SendPIFrameEmbeddingDestructor(mChildProcess);
+    PIFrameEmbeddingParent::Send__delete__(mChildProcess);
     mChildProcess = nsnull;
   }
 #endif
 
   // Let the tree owner know we're gone.
   if (mIsTopLevelContent) {
     nsCOMPtr<nsIDocShellTreeItem> ourItem = do_QueryInterface(mDocShell);
     if (ourItem) {
--- a/content/canvas/public/DocumentRendererParent.h
+++ b/content/canvas/public/DocumentRendererParent.h
@@ -52,16 +52,19 @@ public:
     DocumentRendererParent();
     virtual ~DocumentRendererParent();
 
     void SetCanvasContext(nsICanvasRenderingContextInternal* aCanvas,
 			  gfxContext* ctx);
     void DrawToCanvas(PRUint32 aWidth, PRUint32 aHeight,
 		      const nsCString& aData);
 
+    virtual bool Recv__delete__(const PRUint32& w, const PRUint32& h,
+                                const nsCString& data);
+
 private:
     nsCOMPtr<nsICanvasRenderingContextInternal> mCanvas;
     nsRefPtr<gfxContext> mCanvasContext;
 
     DISALLOW_EVIL_CONSTRUCTORS(DocumentRendererParent);
 };
 
 }
--- a/content/canvas/src/DocumentRendererParent.cpp
+++ b/content/canvas/src/DocumentRendererParent.cpp
@@ -70,8 +70,16 @@ void DocumentRendererParent::DrawToCanva
     mCanvasContext->Fill();
 
     // get rid of the pattern surface ref, because aData is very likely to go away shortly
     mCanvasContext->SetColor(gfxRGBA(1,1,1,1));
 
     gfxRect damageRect = mCanvasContext->UserToDevice(gfxRect(0, 0, aWidth, aHeight));
     mCanvas->Redraw(damageRect);
 }
+
+bool
+DocumentRendererParent::Recv__delete__(const PRUint32& w, const PRUint32& h,
+                                       const nsCString& data)
+{
+    DrawToCanvas(w, h, data);
+    return true;
+}
--- a/dom/ipc/ContentProcessParent.cpp
+++ b/dom/ipc/ContentProcessParent.cpp
@@ -93,17 +93,17 @@ TestShellParent*
 ContentProcessParent::CreateTestShell()
 {
   return static_cast<TestShellParent*>(SendPTestShellConstructor());
 }
 
 bool
 ContentProcessParent::DestroyTestShell(TestShellParent* aTestShell)
 {
-    return SendPTestShellDestructor(aTestShell);
+    return PTestShellParent::Send__delete__(aTestShell);
 }
 
 ContentProcessParent::ContentProcessParent()
     : mMonitor("ContentProcessParent::mMonitor")
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, this);
     mSubprocess->AsyncLaunch();
--- a/dom/ipc/PContentProcess.ipdl
+++ b/dom/ipc/PContentProcess.ipdl
@@ -48,22 +48,19 @@ namespace dom {
 sync protocol PContentProcess
 {
     manages PIFrameEmbedding;
     manages PTestShell;
     manages PNecko;
 
 child:
     PIFrameEmbedding();
-    ~PIFrameEmbedding();
 
     PTestShell();
-    ~PTestShell();
 
     Quit();
 
 parent:
     PNecko();
-    ~PNecko();
 };
 
 }
 }
--- a/dom/ipc/PDocumentRenderer.ipdl
+++ b/dom/ipc/PDocumentRenderer.ipdl
@@ -38,12 +38,15 @@
 include protocol "PIFrameEmbedding.ipdl";
 
 namespace mozilla {
 namespace ipc {
 
 protocol PDocumentRenderer
 {
   manager PIFrameEmbedding;
+
+parent:
+    __delete__(PRUint32 w, PRUint32 h, nsCString data);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/dom/ipc/PIFrameEmbedding.ipdl
+++ b/dom/ipc/PIFrameEmbedding.ipdl
@@ -38,29 +38,30 @@
  * ***** END LICENSE BLOCK ***** */
 
 include protocol "PContentProcess.ipdl";
 include protocol "PDocumentRenderer.ipdl";
 
 include "mozilla/TabTypes.h";
 include "TabMessageUtils.h";
 
-using PRUint32;
-using PRInt32;
 using MagicWindowHandle;
 using RemoteDOMEvent;
 
 namespace mozilla {
 namespace dom {
 
 async protocol PIFrameEmbedding
 {
     manager PContentProcess;
     manages PDocumentRenderer;
 
+child:
+    __delete__();
+
 parent:
     /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element.
      */
     moveFocus(bool forward);
 
     sendEvent(RemoteDOMEvent aEvent);
@@ -91,16 +92,12 @@ child:
                    bool aIgnoreRootScrollFrame);
 
     /**
      * Activate event forwarding from client to parent.
      */
     activateFrameEvent(nsString aType, bool capture);
 
     PDocumentRenderer(PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h, nsString bgcolor, PRUint32 flags, bool flush);
-
-parent:
-
-    ~PDocumentRenderer(PRUint32 w, PRUint32 h, nsCString data);
 };
 
 }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -356,23 +356,19 @@ TabChild::AllocPDocumentRenderer(
         const nsString& bgcolor,
         const PRUint32& flags,
         const bool& flush)
 {
     return new mozilla::ipc::DocumentRendererChild();
 }
 
 bool
-TabChild::DeallocPDocumentRenderer(
-        mozilla::ipc::PDocumentRendererChild* __a,
-        const PRUint32& w,
-        const PRUint32& h,
-        const nsCString& data)
+TabChild::DeallocPDocumentRenderer(PDocumentRendererChild* actor)
 {
-    delete __a;
+    delete actor;
     return true;
 }
 
 bool
 TabChild::RecvPDocumentRendererConstructor(
         mozilla::ipc::PDocumentRendererChild *__a,
         const PRInt32& aX,
         const PRInt32& aY,
@@ -397,17 +393,17 @@ TabChild::RecvPDocumentRendererConstruct
 
     PRUint32 width, height;
     nsCString data;
     bool ret = render->RenderDocument(window, aX, aY, aW, aH, bgcolor, flags, flush,
                                       width, height, data);
     if (!ret)
         return true; // silently ignore
 
-    return SendPDocumentRendererDestructor(__a, width, height, data);
+    return PDocumentRendererChild::Send__delete__(__a, width, height, data);
 }
 
 bool
 TabChild::RecvactivateFrameEvent(const nsString& aType, const bool& capture)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
   NS_ENSURE_TRUE(window, true);
   nsCOMPtr<nsIDOMEventTarget> chromeHandler =
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -100,21 +100,17 @@ public:
     virtual mozilla::ipc::PDocumentRendererChild* AllocPDocumentRenderer(
             const PRInt32& x,
             const PRInt32& y,
             const PRInt32& w,
             const PRInt32& h,
             const nsString& bgcolor,
             const PRUint32& flags,
             const bool& flush);
-    virtual bool DeallocPDocumentRenderer(
-            mozilla::ipc::PDocumentRendererChild* __a,
-            const PRUint32& w,
-            const PRUint32& h,
-            const nsCString& data);
+    virtual bool DeallocPDocumentRenderer(PDocumentRendererChild* actor);
     virtual bool RecvPDocumentRendererConstructor(
             mozilla::ipc::PDocumentRendererChild *__a,
             const PRInt32& x,
             const PRInt32& y,
             const PRInt32& w,
             const PRInt32& h,
             const nsString& bgcolor,
             const PRUint32& flags,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -118,32 +118,19 @@ mozilla::ipc::PDocumentRendererParent*
 TabParent::AllocPDocumentRenderer(const PRInt32& x,
         const PRInt32& y, const PRInt32& w, const PRInt32& h, const nsString& bgcolor,
         const PRUint32& flags, const bool& flush)
 {
     return new DocumentRendererParent();
 }
 
 bool
-TabParent::DeallocPDocumentRenderer(mozilla::ipc::PDocumentRendererParent* __a,
-        const PRUint32& w, const PRUint32& h, const nsCString& data)
+TabParent::DeallocPDocumentRenderer(PDocumentRendererParent* actor)
 {
-    NS_ENSURE_ARG_POINTER(__a);
-    delete __a;
-    return true;
-}
-
-bool
-TabParent::RecvPDocumentRendererDestructor(PDocumentRendererParent* __a,
-        const PRUint32& w, const PRUint32& h, const nsCString& data)
-{
-    NS_ENSURE_ARG_POINTER(__a);
-    DocumentRendererParent *renderer = static_cast<DocumentRendererParent *>(__a);
-    renderer->DrawToCanvas(w, h, data);
-
+    delete actor;
     return true;
 }
 
 void
 TabParent::SendMouseEvent(const nsAString& aType, float aX, float aY,
                           PRInt32 aButton, PRInt32 aClickCount,
                           PRInt32 aModifiers, PRBool aIgnoreRootScrollFrame)
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -69,26 +69,17 @@ public:
     virtual mozilla::ipc::PDocumentRendererParent* AllocPDocumentRenderer(
             const PRInt32& x,
             const PRInt32& y,
             const PRInt32& w,
             const PRInt32& h,
             const nsString& bgcolor,
             const PRUint32& flags,
             const bool& flush);
-    virtual bool DeallocPDocumentRenderer(
-            mozilla::ipc::PDocumentRendererParent* __a,
-            const PRUint32& w,
-            const PRUint32& h,
-            const nsCString& data);
-    virtual bool RecvPDocumentRendererDestructor(
-            mozilla::ipc::PDocumentRendererParent* __a,
-            const PRUint32& w,
-            const PRUint32& h,
-            const nsCString& data);
+    virtual bool DeallocPDocumentRenderer(PDocumentRendererParent* actor);
 protected:
     nsIDOMElement* mFrameElement;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/plugins/BrowserStreamChild.cpp
+++ b/dom/plugins/BrowserStreamChild.cpp
@@ -122,16 +122,26 @@ BrowserStreamChild::AnswerNPP_StreamAsFi
   if (mClosed)
     return true;
 
   mInstance->mPluginIface->asfile(&mInstance->mData, &mStream,
                                   fname.get());
   return true;
 }
 
+bool
+BrowserStreamChild::Answer__delete__(const NPError& reason,
+                                     const bool& artificial)
+{
+  AssertPluginThread();
+  if (!artificial)
+    NPP_DestroyStream(reason);
+  return true;
+}
+
 NPError
 BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
 {
   AssertPluginThread();
 
   IPCByteRanges ranges;
   for (; aRangeList; aRangeList = aRangeList->next) {
     IPCByteRange br = {aRangeList->offset, aRangeList->length};
--- a/dom/plugins/BrowserStreamChild.h
+++ b/dom/plugins/BrowserStreamChild.h
@@ -66,16 +66,19 @@ public:
 
   virtual bool AnswerNPP_WriteReady(const int32_t& newlength,
                                         int32_t *size);
   virtual bool AnswerNPP_Write(const int32_t& offset,
                                    const Buffer& data,
                                    int32_t* consumed);
 
   virtual bool AnswerNPP_StreamAsFile(const nsCString& fname);
+  virtual bool Answer__delete__(const NPError& reason,
+                                const bool& artificial);
+
 
   void EnsureCorrectInstance(PluginInstanceChild* i)
   {
     if (i != mInstance)
       NS_RUNTIMEABORT("Incorrect stream instance");
   }
   void EnsureCorrectStream(NPStream* s)
   {
--- a/dom/plugins/BrowserStreamParent.cpp
+++ b/dom/plugins/BrowserStreamParent.cpp
@@ -39,16 +39,25 @@ BrowserStreamParent::AnswerNPN_RequestRe
     rp[i].next = &rp[i + 1];
   }
   rp[ranges.size() - 1].next = NULL;
 
   *result = mNPP->mNPNIface->requestread(mStream, rp);
   return true;
 }
 
+bool
+BrowserStreamParent::Answer__delete__(const NPError& reason,
+                                      const bool& artificial)
+{
+  if (!artificial)
+    NPN_DestroyStream(reason);
+  return true;
+}
+
 int32_t
 BrowserStreamParent::WriteReady()
 {
   _MOZ_LOG(__FUNCTION__);
 
   int32_t result;
   if (!CallNPP_WriteReady(mStream->end, &result))
     return -1;
--- a/dom/plugins/BrowserStreamParent.h
+++ b/dom/plugins/BrowserStreamParent.h
@@ -56,22 +56,26 @@ public:
                       NPStream* stream);
   virtual ~BrowserStreamParent();
 
   NS_OVERRIDE virtual bool IsBrowserStream() { return true; }
 
   virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges,
                                      NPError* result);
 
+  virtual bool
+  Answer__delete__(const NPError& reason, const bool& artificial);
+
   int32_t WriteReady();
   int32_t Write(int32_t offset, int32_t len, void* buffer);
   void StreamAsFile(const char* fname);
+
+private:
   NPError NPN_DestroyStream(NPError reason);
 
-private:
   PluginInstanceParent* mNPP;
   NPStream* mStream;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif
--- a/dom/plugins/PBrowserStream.ipdl
+++ b/dom/plugins/PBrowserStream.ipdl
@@ -38,16 +38,17 @@
 include protocol "PPluginInstance.ipdl";
 
 include "mozilla/plugins/PluginMessageUtils.h";
 
 using mozilla::plugins::Buffer;
 using mozilla::plugins::IPCByteRanges;
 
 using NPError;
+using NPReason;
 
 namespace mozilla {
 namespace plugins {
 
 /**
  * NPBrowserStream represents a NPStream sent from the browser to the plugin.
  */
 
@@ -63,12 +64,20 @@ child:
                 Buffer data)
     returns (int32_t consumed);
 
   rpc NPP_StreamAsFile(nsCString fname);
 
 parent:
   rpc NPN_RequestRead(IPCByteRanges ranges)
     returns (NPError result);
+
+both:
+  /**
+   * ~PBrowserStream is for both NPN_DestroyStream and NPP_DestroyStream.
+   * @param artificial True when the stream is closed as a by-product of
+   *                        some other call (such as a failure in NPP_Write).
+   */
+  rpc __delete__(NPReason reason, bool artificial);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PPluginInstance.ipdl
+++ b/dom/plugins/PPluginInstance.ipdl
@@ -43,32 +43,34 @@ include protocol "PBrowserStream.ipdl";
 include protocol "PPluginStream.ipdl";
 include protocol "PStreamNotify.ipdl";
 
 include "mozilla/plugins/PluginMessageUtils.h";
 
 using NPError;
 using NPRemoteWindow;
 using NPRemoteEvent;
-using NPReason;
 using NPRect;
 
 namespace mozilla {
 namespace plugins {
 
 rpc protocol PPluginInstance
 {
   manager PPluginModule;
 
   manages PPluginScriptableObject;
   manages PBrowserStream;
   manages PPluginStream;
   manages PStreamNotify;
 
 child:
+  rpc __delete__()
+    returns (NPError rv);
+
   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);
   // this message is not used on non-X platforms
@@ -114,56 +116,32 @@ parent:
   async NPN_InvalidateRect(NPRect rect);
 
   rpc NPN_PushPopupsEnabledState(bool aState)
     returns (bool aSuccess);
 
   rpc NPN_PopPopupsEnabledState()
     returns (bool aSuccess);
 
-child:
-  /**
-   * Represents NPP_URLNotify
-   */
-  rpc ~PStreamNotify(NPReason reason);
-
 both:
   rpc PPluginScriptableObject();
-  rpc ~PPluginScriptableObject();
 
 child:
   /* NPP_NewStream */
   rpc PBrowserStream(nsCString url,
                      uint32_t length,
                      uint32_t lastmodified,
                      nullable PStreamNotify notifyData,
                      nsCString headers,
                      nsCString mimeType,
                      bool seekable)
     returns (NPError rv,
              uint16_t stype);
 
-both:
-  /**
-   * ~PBrowserStream is for both NPN_DestroyStream and NPP_DestroyStream.
-   * @param artificial True when the stream is closed as a by-product of
-   *                        some other call (such as a failure in NPP_Write).
-   */
-  rpc ~PBrowserStream(NPReason reason,
-                      bool artificial);
-
 parent:
   /* NPN_NewStream */
   rpc PPluginStream(nsCString mimeType,
                     nsCString target)
     returns (NPError result);
-
-both:
-  /**
-   * ~PPluginStream is for both NPN_DestroyStream and NPP_DestroyStream.
-   * @param artificial True when the stream is closed as a by-product of
-   *                        some other call (such as a failure in NPN_Write).
-   */
-  rpc ~PPluginStream(NPReason reason, bool artificial);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PPluginModule.ipdl
+++ b/dom/plugins/PPluginModule.ipdl
@@ -58,19 +58,16 @@ child:
     returns (NPError rv);
 
   rpc PPluginInstance(nsCString aMimeType,
                       uint16_t aMode,
                       nsCString[] aNames,
                       nsCString[] aValues)
     returns (NPError rv);
 
-  rpc ~PPluginInstance()
-    returns (NPError rv);
-
   rpc NP_Shutdown()
     returns (NPError rv);
 
 parent:
   rpc NPN_UserAgent()
     returns (nsCString userAgent);
 
   sync NPN_GetStringIdentifier(nsCString aString)
--- a/dom/plugins/PPluginScriptableObject.ipdl
+++ b/dom/plugins/PPluginScriptableObject.ipdl
@@ -59,16 +59,19 @@ union Variant {
   nsCString;
   nullable PPluginScriptableObject;
 };
 
 rpc protocol PPluginScriptableObject
 {
   manager PPluginInstance;
 
+both:
+  rpc __delete__();
+
 parent:
   rpc NPN_Evaluate(nsCString aScript)
     returns (Variant aResult,
              bool aSuccess);
 
 both:
   // NPClass methods
   rpc Invalidate();
--- a/dom/plugins/PPluginStream.ipdl
+++ b/dom/plugins/PPluginStream.ipdl
@@ -36,26 +36,35 @@
  * ***** END LICENSE BLOCK ***** */
 
 include protocol "PPluginInstance.ipdl";
 
 include "mozilla/plugins/PluginMessageUtils.h";
 
 using mozilla::plugins::Buffer;
 using NPError;
+using NPReason;
 
 namespace mozilla {
 namespace plugins {
 
 /**
  * PPluginStream represents an NPStream sent from the plugin to the browser.
  */
 
 rpc protocol PPluginStream
 {
   manager PPluginInstance;
 
 parent:
   rpc NPN_Write(Buffer data) returns (int32_t written);
+
+both:
+  /**
+   * ~PPluginStream is for both NPN_DestroyStream and NPP_DestroyStream.
+   * @param artificial True when the stream is closed as a by-product of
+   *                        some other call (such as a failure in NPN_Write).
+   */
+  rpc __delete__(NPReason reason, bool artificial);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PStreamNotify.ipdl
+++ b/dom/plugins/PStreamNotify.ipdl
@@ -1,17 +1,27 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 
 include protocol "PPluginInstance.ipdl";
 
+include "npapi.h";
+
+using NPReason;
+
 namespace mozilla {
 namespace plugins {
 
 /**
  * This empty protocol exists only to be constructed and destroyed.
  */
 rpc protocol PStreamNotify
 {
   manager PPluginInstance;
+
+child:
+  /**
+   * Represents NPP_URLNotify
+   */
+  rpc __delete__(NPReason reason);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -82,16 +82,24 @@ PluginInstanceChild::PluginInstanceChild
 
 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)
 {
     printf ("[PluginInstanceChild] NPN_GetValue(%s)\n",
             NPNVariableToString(aVar));
     AssertPluginThread();
 
@@ -716,17 +724,17 @@ PluginInstanceChild::AllocPPluginScripta
     *object = new PluginScriptableObjectChild();
     NS_ENSURE_TRUE(*object, nsnull);
 
     return object->get();
 }
 
 bool
 PluginInstanceChild::DeallocPPluginScriptableObject(
-                                          PPluginScriptableObjectChild* aObject)
+    PPluginScriptableObjectChild* aObject)
 {
     AssertPluginThread();
 
     PluginScriptableObjectChild* object =
         reinterpret_cast<PluginScriptableObjectChild*>(aObject);
 
     NPObject* npobject = object->GetObject();
     if (npobject &&
@@ -783,61 +791,34 @@ PluginInstanceChild::AllocPBrowserStream
                                          uint16_t *stype)
 {
     AssertPluginThread();
     return new BrowserStreamChild(this, url, length, lastmodified, notifyData,
                                   headers, mimeType, seekable, rv, stype);
 }
 
 bool
-PluginInstanceChild::AnswerPBrowserStreamDestructor(PBrowserStreamChild* stream,
-                                                    const NPError& reason,
-                                                    const bool& artificial)
-{
-    AssertPluginThread();
-    if (!artificial)
-        static_cast<BrowserStreamChild*>(stream)->NPP_DestroyStream(reason);
-    return true;
-}
-
-bool
-PluginInstanceChild::DeallocPBrowserStream(PBrowserStreamChild* stream,
-                                           const NPError& reason,
-                                           const bool& artificial)
+PluginInstanceChild::DeallocPBrowserStream(PBrowserStreamChild* stream)
 {
     AssertPluginThread();
     delete stream;
     return true;
 }
 
 PPluginStreamChild*
 PluginInstanceChild::AllocPPluginStream(const nsCString& mimeType,
                                         const nsCString& target,
                                         NPError* result)
 {
     NS_RUNTIMEABORT("not callable");
     return NULL;
 }
 
 bool
-PluginInstanceChild::AnswerPPluginStreamDestructor(PPluginStreamChild* stream,
-                                                   const NPReason& reason,
-                                                   const bool& artificial)
-{
-    AssertPluginThread();
-    if (!artificial) {
-        static_cast<PluginStreamChild*>(stream)->NPP_DestroyStream(reason);
-    }
-    return true;
-}
-
-bool
-PluginInstanceChild::DeallocPPluginStream(PPluginStreamChild* stream,
-                                          const NPError& reason,
-                                          const bool& artificial)
+PluginInstanceChild::DeallocPPluginStream(PPluginStreamChild* stream)
 {
     AssertPluginThread();
     delete stream;
     return true;
 }
 
 PStreamNotifyChild*
 PluginInstanceChild::AllocPStreamNotify(const nsCString& url,
@@ -848,32 +829,37 @@ PluginInstanceChild::AllocPStreamNotify(
                                         NPError* result)
 {
     AssertPluginThread();
     NS_RUNTIMEABORT("not reached");
     return NULL;
 }
 
 bool
-PluginInstanceChild::AnswerPStreamNotifyDestructor(PStreamNotifyChild* notifyData,
-                                                   const NPReason& reason)
+StreamNotifyChild::Answer__delete__(const NPReason& reason)
 {
     AssertPluginThread();
+    return static_cast<PluginInstanceChild*>(Manager())
+        ->NotifyStream(this, reason);
+}
 
-    StreamNotifyChild* sn = static_cast<StreamNotifyChild*>(notifyData);
-    if (sn->mClosure)
-        mPluginIface->urlnotify(&mData, sn->mURL.get(), reason, sn->mClosure);
-
+bool
+PluginInstanceChild::NotifyStream(StreamNotifyChild* notifyData,
+                                  NPReason reason)
+{
+    if (notifyData->mClosure)
+        mPluginIface->urlnotify(&mData, notifyData->mURL.get(), reason,
+                                notifyData->mClosure);
     return true;
 }
 
 bool
-PluginInstanceChild::DeallocPStreamNotify(PStreamNotifyChild* notifyData,
-                                          const NPReason& reason)
+PluginInstanceChild::DeallocPStreamNotify(PStreamNotifyChild* notifyData)
 {
+    AssertPluginThread();
     delete notifyData;
     return true;
 }
 
 PluginScriptableObjectChild*
 PluginInstanceChild::GetActorForNPObject(NPObject* aObject)
 {
     AssertPluginThread();
@@ -909,24 +895,24 @@ PluginInstanceChild::GetActorForNPObject
 }
 
 NPError
 PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
                                    NPStream** aStream)
 {
     AssertPluginThread();
 
-    PluginStreamChild* ps = new PluginStreamChild(this);
+    PluginStreamChild* ps = new PluginStreamChild();
 
     NPError result;
     CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType),
                                  NullableString(aWindow), &result);
     if (NPERR_NO_ERROR != result) {
         *aStream = NULL;
-        CallPPluginStreamDestructor(ps, NPERR_GENERIC_ERROR, true);
+        PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true);
         return result;
     }
 
     *aStream = &ps->mStream;
     return NPERR_NO_ERROR;
 }
 
 bool
--- a/dom/plugins/PluginInstanceChild.h
+++ b/dom/plugins/PluginInstanceChild.h
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef dom_plugins_PluginInstanceChild_h
 #define dom_plugins_PluginInstanceChild_h 1
 
 #include "mozilla/plugins/PPluginInstanceChild.h"
 #include "mozilla/plugins/PluginScriptableObjectChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
 #if defined(OS_WIN)
 #include "mozilla/gfx/SharedDIBWin.h"
 #endif
 
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 
@@ -68,16 +69,18 @@ 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
@@ -103,53 +106,34 @@ protected:
                         PStreamNotifyChild* notifyData,
                         const nsCString& headers,
                         const nsCString& mimeType,
                         const bool& seekable,
                         NPError* rv,
                         uint16_t *stype);
 
     virtual bool
-    AnswerPBrowserStreamDestructor(PBrowserStreamChild* stream,
-                                   const NPError& reason,
-                                   const bool& artificial);
-
-    virtual bool
-    DeallocPBrowserStream(PBrowserStreamChild* stream,
-                          const NPError& reason,
-                          const bool& artificial);
+    DeallocPBrowserStream(PBrowserStreamChild* stream);
 
     virtual PPluginStreamChild*
     AllocPPluginStream(const nsCString& mimeType,
                        const nsCString& target,
                        NPError* result);
 
     virtual bool
-    AnswerPPluginStreamDestructor(PPluginStreamChild* stream,
-                                  const NPReason& reason,
-                                  const bool& artificial);
-
-    virtual bool
-    DeallocPPluginStream(PPluginStreamChild* stream,
-                         const NPReason& reason,
-                         const bool& artificial);
+    DeallocPPluginStream(PPluginStreamChild* stream);
 
     virtual PStreamNotifyChild*
     AllocPStreamNotify(const nsCString& url, const nsCString& target,
                        const bool& post, const nsCString& buffer,
                        const bool& file,
                        NPError* result);
 
     NS_OVERRIDE virtual bool
-    AnswerPStreamNotifyDestructor(PStreamNotifyChild* notifyData,
-                                  const NPReason& reason);
-
-    NS_OVERRIDE virtual bool
-    DeallocPStreamNotify(PStreamNotifyChild* notifyData,
-                         const NPReason& reason);
+    DeallocPStreamNotify(PStreamNotifyChild* notifyData);
 
 public:
     PluginInstanceChild(const NPPluginFuncs* aPluginIface);
 
     virtual ~PluginInstanceChild();
 
     bool Initialize();
     void Destroy();
@@ -172,16 +156,18 @@ public:
     NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
                   NPStream** aStream);
 
     // Return true if you want to send the notification to the parent process
     // also.
     bool
     InternalInvalidateRect(NPRect* aInvalidRect);
 
+    bool NotifyStream(StreamNotifyChild* notifyData, NPReason reason);
+
 private:
 
 #if defined(OS_WIN)
     static bool RegisterWindowClass();
     bool CreatePluginWindow();
     void DestroyPluginWindow();
     void ReparentPluginWindow(HWND hWndParent);
     void SizePluginWindow(int width, int height);
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -109,51 +109,33 @@ PluginInstanceParent::AllocPBrowserStrea
                                           NPError* rv,
                                           uint16_t *stype)
 {
     NS_RUNTIMEABORT("Not reachable");
     return NULL;
 }
 
 bool
-PluginInstanceParent::AnswerPBrowserStreamDestructor(PBrowserStreamParent* stream,
-                                                     const NPError& reason,
-                                                     const bool& artificial)
-{
-    if (!artificial) {
-        static_cast<BrowserStreamParent*>(stream)->NPN_DestroyStream(reason);
-    }
-    return true;
-}
-
-bool
-PluginInstanceParent::DeallocPBrowserStream(PBrowserStreamParent* stream,
-                                            const NPError& reason,
-                                            const bool& artificial)
+PluginInstanceParent::DeallocPBrowserStream(PBrowserStreamParent* stream)
 {
     delete stream;
     return true;
 }
 
 PPluginStreamParent*
 PluginInstanceParent::AllocPPluginStream(const nsCString& mimeType,
                                          const nsCString& target,
                                          NPError* result)
 {
     return new PluginStreamParent(this, mimeType, target, result);
 }
 
 bool
-PluginInstanceParent::DeallocPPluginStream(PPluginStreamParent* stream,
-                                           const NPError& reason,
-                                           const bool& artificial)
+PluginInstanceParent::DeallocPPluginStream(PPluginStreamParent* stream)
 {
-    if (!artificial) {
-        static_cast<PluginStreamParent*>(stream)->NPN_DestroyStream(reason);
-    }
     delete stream;
     return true;
 }
 
 bool
 PluginInstanceParent::AnswerNPN_GetValue_NPNVjavascriptEnabledBool(
                                                        bool* value,
                                                        NPError* result)
@@ -305,24 +287,23 @@ PluginInstanceParent::AnswerPStreamNotif
                                            NullableStringGet(url),
                                            NullableStringGet(target),
                                            buffer.Length(),
                                            NullableStringGet(buffer),
                                            file, actor);
     }
 
     if (*result != NPERR_NO_ERROR)
-        CallPStreamNotifyDestructor(actor, NPERR_GENERIC_ERROR);
+        PStreamNotifyParent::Call__delete__(actor, NPERR_GENERIC_ERROR);
 
     return true;
 }
 
 bool
-PluginInstanceParent::DeallocPStreamNotify(PStreamNotifyParent* notifyData,
-                                           const NPReason& reason)
+PluginInstanceParent::DeallocPStreamNotify(PStreamNotifyParent* notifyData)
 {
     delete notifyData;
     return true;
 }
 
 bool
 PluginInstanceParent::RecvNPN_InvalidateRect(const NPRect& rect)
 {
@@ -542,43 +523,43 @@ PluginInstanceParent::NPP_NewStream(NPMI
                                        stream->lastmodified,
                                        static_cast<PStreamNotifyParent*>(stream->notifyData),
                                        NullableString(stream->headers),
                                        NullableString(type), seekable,
                                        &err, stype))
         return NPERR_GENERIC_ERROR;
 
     if (NPERR_NO_ERROR != err)
-        CallPBrowserStreamDestructor(bs, NPERR_GENERIC_ERROR, true);
+        PBrowserStreamParent::Call__delete__(bs, NPERR_GENERIC_ERROR, true);
 
     return err;
 }
 
 NPError
 PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason)
 {
     _MOZ_LOG(__FUNCTION__);
 
     AStream* s = static_cast<AStream*>(stream->pdata);
     if (s->IsBrowserStream()) {
         BrowserStreamParent* sp =
             static_cast<BrowserStreamParent*>(s);
         if (sp->mNPP != this)
             NS_RUNTIMEABORT("Mismatched plugin data");
 
-        CallPBrowserStreamDestructor(sp, reason, false);
+        PBrowserStreamParent::Call__delete__(sp, reason, false);
         return NPERR_NO_ERROR;
     }
     else {
         PluginStreamParent* sp =
             static_cast<PluginStreamParent*>(s);
         if (sp->mInstance != this)
             NS_RUNTIMEABORT("Mismatched plugin data");
 
-        CallPPluginStreamDestructor(sp, reason, false);
+        PPluginStreamParent::Call__delete__(sp, reason, false);
         return NPERR_NO_ERROR;
     }
 }
 
 PPluginScriptableObjectParent*
 PluginInstanceParent::AllocPPluginScriptableObject()
 {
     nsAutoPtr<PluginScriptableObjectParent>* object =
@@ -640,17 +621,17 @@ PluginInstanceParent::AnswerPPluginScrip
 void
 PluginInstanceParent::NPP_URLNotify(const char* url, NPReason reason,
                                     void* notifyData)
 {
     _MOZ_LOG(__FUNCTION__);
 
     PStreamNotifyParent* streamNotify =
         static_cast<PStreamNotifyParent*>(notifyData);
-    CallPStreamNotifyDestructor(streamNotify, reason);
+    PStreamNotifyParent::Call__delete__(streamNotify, reason);
 }
 
 PluginScriptableObjectParent*
 PluginInstanceParent::GetActorForNPObject(NPObject* aObject)
 {
     NS_ASSERTION(aObject, "Null pointer!");
 
     if (aObject->_class == PluginScriptableObjectParent::GetClass()) {
--- a/dom/plugins/PluginInstanceParent.h
+++ b/dom/plugins/PluginInstanceParent.h
@@ -52,17 +52,16 @@
 
 #undef _MOZ_LOG
 #define _MOZ_LOG(s) printf("[PluginInstanceParent] %s\n", s)
 
 namespace mozilla {
 namespace plugins {
 
 class PBrowserStreamParent;
-class BrowserStreamParent;
 class PluginModuleParent;
 
 class PluginInstanceParent : public PPluginInstanceParent
 {
     friend class PluginModuleParent;
     friend class BrowserStreamParent;
     friend class PluginStreamParent;
 
@@ -88,36 +87,25 @@ public:
                         const uint32_t& length,
                         const uint32_t& lastmodified,
                         PStreamNotifyParent* notifyData,
                         const nsCString& headers,
                         const nsCString& mimeType,
                         const bool& seekable,
                         NPError* rv,
                         uint16_t *stype);
-
     virtual bool
-    AnswerPBrowserStreamDestructor(PBrowserStreamParent* stream,
-                                   const NPError& reason,
-                                   const bool& artificial);
-
-    virtual bool
-    DeallocPBrowserStream(PBrowserStreamParent* stream,
-                          const NPError& reason,
-                          const bool& artificial);
+    DeallocPBrowserStream(PBrowserStreamParent* stream);
 
     virtual PPluginStreamParent*
     AllocPPluginStream(const nsCString& mimeType,
                        const nsCString& target,
                        NPError* result);
-
     virtual bool
-    DeallocPPluginStream(PPluginStreamParent* stream,
-                         const NPError& reason,
-                         const bool& artificial);
+    DeallocPPluginStream(PPluginStreamParent* stream);
 
     virtual bool
     AnswerNPN_GetValue_NPNVjavascriptEnabledBool(bool* value, NPError* result);
     virtual bool
     AnswerNPN_GetValue_NPNVisOfflineBool(bool* value, NPError* result);
     virtual bool
     AnswerNPN_GetValue_NPNVWindowNPObject(
                                        PPluginScriptableObjectParent** value,
@@ -154,18 +142,17 @@ public:
     AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor,
                                    const nsCString& url,
                                    const nsCString& target,
                                    const bool& post, const nsCString& buffer,
                                    const bool& file,
                                    NPError* result);
 
     virtual bool
-    DeallocPStreamNotify(PStreamNotifyParent* notifyData,
-                         const NPReason& reason);
+    DeallocPStreamNotify(PStreamNotifyParent* notifyData);
 
     virtual bool
     RecvNPN_InvalidateRect(const NPRect& rect);
 
     virtual bool
     AnswerNPN_PushPopupsEnabledState(const bool& aState,
                                      bool* aSuccess);
 
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -733,22 +733,22 @@ NPError NP_CALLBACK
     _MOZ_LOG(__FUNCTION__);
     AssertPluginThread();
 
     PluginInstanceChild* p = InstCast(aNPP);
     AStream* s = static_cast<AStream*>(aStream->ndata);
     if (s->IsBrowserStream()) {
         BrowserStreamChild* bs = static_cast<BrowserStreamChild*>(s);
         bs->EnsureCorrectInstance(p);
-        p->CallPBrowserStreamDestructor(bs, aReason, false);
+        PBrowserStreamChild::Call__delete__(bs, aReason, false);
     }
     else {
         PluginStreamChild* ps = static_cast<PluginStreamChild*>(s);
         ps->EnsureCorrectInstance(p);
-        p->CallPPluginStreamDestructor(ps, aReason, false);
+        PPluginStreamChild::Call__delete__(ps, aReason, false);
     }
     return NPERR_NO_ERROR;
 }
 
 void NP_CALLBACK
 _status(NPP aNPP,
         const char* aMessage)
 {
@@ -1437,33 +1437,31 @@ PluginModuleChild::AnswerPPluginInstance
         return false;
     }
 
     printf ("[PluginModuleChild] %s: returning %hd\n", __FUNCTION__, *rv);
     return true;
 }
 
 bool
-PluginModuleChild::DeallocPPluginInstance(PPluginInstanceChild* aActor,
-                                          NPError* rv)
+PluginModuleChild::DeallocPPluginInstance(PPluginInstanceChild* aActor)
 {
     _MOZ_LOG(__FUNCTION__);
     AssertPluginThread();
 
     delete aActor;
 
     return true;
 }
 
 bool
-PluginModuleChild::AnswerPPluginInstanceDestructor(PPluginInstanceChild* aActor,
-                                                   NPError* rv)
+PluginModuleChild::PluginInstanceDestroyed(PluginInstanceChild* aActor,
+                                           NPError* rv)
 {
     _MOZ_LOG(__FUNCTION__);
     AssertPluginThread();
 
-    PluginInstanceChild* inst = static_cast<PluginInstanceChild*>(aActor);
-    *rv = mFunctions.destroy(inst->GetNPP(), 0);
-    inst->Destroy();
-    inst->GetNPP()->ndata = 0;
+    *rv = mFunctions.destroy(aActor->GetNPP(), 0);
+    aActor->Destroy();
+    aActor->GetNPP()->ndata = 0;
 
     return true;
 }
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -104,22 +104,17 @@ protected:
     virtual PPluginInstanceChild*
     AllocPPluginInstance(const nsCString& aMimeType,
                          const uint16_t& aMode,
                          const nsTArray<nsCString>& aNames,
                          const nsTArray<nsCString>& aValues,
                          NPError* rv);
 
     virtual bool
-    DeallocPPluginInstance(PPluginInstanceChild* aActor,
-                           NPError* rv);
-
-    virtual bool
-    AnswerPPluginInstanceDestructor(PPluginInstanceChild* aActor,
-                                    NPError* rv);
+    DeallocPPluginInstance(PPluginInstanceChild* aActor);
 
     virtual bool
     AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor,
                                      const nsCString& aMimeType,
                                      const uint16_t& aMode,
                                      const nsTArray<nsCString>& aNames,
                                      const nsTArray<nsCString>& aValues,
                                      NPError* rv);
@@ -149,16 +144,20 @@ public:
     void UnregisterNPObject(NPObject* aObject);
 
     PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
 
 #ifdef DEBUG
     bool NPObjectIsRegisteredForActor(PluginScriptableObjectChild* aActor);
 #endif
 
+    bool
+    PluginInstanceDestroyed(PluginInstanceChild* aActor,
+                            NPError* rv);
+
 private:
     bool InitGraphics();
 
     std::string mPluginFilename;
     PRLibrary* mLibrary;
     nsCString mUserAgent;
 
     // we get this from the plugin
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -92,22 +92,20 @@ PluginModuleParent::AllocPPluginInstance
                                          const nsTArray<nsCString>& aValues,
                                          NPError* rv)
 {
     NS_ERROR("Not reachable!");
     return NULL;
 }
 
 bool
-PluginModuleParent::DeallocPPluginInstance(PPluginInstanceParent* aActor,
-                                           NPError* _retval)
+PluginModuleParent::DeallocPPluginInstance(PPluginInstanceParent* aActor)
 {
     _MOZ_LOG(__FUNCTION__);
     delete aActor;
-    *_retval = NPERR_NO_ERROR;
     return true;
 }
 
 void
 PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs)
 {
     aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
     aFuncs->javaClass = nsnull;
@@ -142,17 +140,17 @@ PluginModuleParent::NPP_Destroy(NPP inst
         static_cast<PluginInstanceParent*>(instance->pdata);
 
     if (!parentInstance)
         return NPERR_NO_ERROR;
 
     parentInstance->Destroy();
 
     NPError prv;
-    if (!parentInstance->Module()->CallPPluginInstanceDestructor(parentInstance, &prv)) {
+    if (!PPluginInstanceParent::Call__delete__(parentInstance, &prv)) {
         prv = NPERR_GENERIC_ERROR;
     }
     instance->pdata = nsnull;
 
     return prv;
 }
 
 bool
@@ -551,17 +549,17 @@ PluginModuleParent::NPP_New(NPMIMEType p
             *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
     printf ("[PluginModuleParent] %s: got return value %hd\n", __FUNCTION__,
             *error);
 
     if (*error != NPERR_NO_ERROR) {
-        CallPPluginInstanceDestructor(parentInstance, error);
+        PPluginInstanceParent::Call__delete__(parentInstance, error);
         instance->pdata = nsnull;
         return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
 }
 
 bool
--- a/dom/plugins/PluginModuleParent.h
+++ b/dom/plugins/PluginModuleParent.h
@@ -61,16 +61,18 @@
 
 #undef _MOZ_LOG
 #define _MOZ_LOG(s) printf("[PluginModuleParent] %s\n", s)
 
 namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
+class BrowserStreamParent;
+
 /**
  * PluginModuleParent
  *
  * This class implements the NPP API from the perspective of the rest
  * of Gecko, forwarding NPP calls along to the child process that is
  * actually running the plugin.
  *
  * This class /also/ implements a version of the NPN API, because the
@@ -86,18 +88,17 @@ protected:
     PPluginInstanceParent*
     AllocPPluginInstance(const nsCString& aMimeType,
                          const uint16_t& aMode,
                          const nsTArray<nsCString>& aNames,
                          const nsTArray<nsCString>& aValues,
                          NPError* rv);
 
     virtual bool
-    DeallocPPluginInstance(PPluginInstanceParent* aActor,
-                           NPError* _retval);
+    DeallocPPluginInstance(PPluginInstanceParent* aActor);
 
 public:
     PluginModuleParent(const char* aFilePath);
 
     virtual ~PluginModuleParent();
 
     /**
      * LoadModule
--- a/dom/plugins/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/PluginScriptableObjectChild.cpp
@@ -211,17 +211,17 @@ PluginScriptableObjectChild::ScriptableI
 
   if (actor && !actor->CallInvalidate()) {
     NS_WARNING("Failed to send message!");
   }
 
   object->invalidated = true;
 
   if (instance &&
-      !instance->CallPPluginScriptableObjectDestructor(object->parent)) {
+      !PPluginScriptableObjectChild::Call__delete__(object->parent)) {
     NS_WARNING("Failed to send message!");
   }
 }
 
 // static
 void
 PluginScriptableObjectChild::ScriptableDeallocate(NPObject* aObject)
 {
--- a/dom/plugins/PluginScriptableObjectParent.cpp
+++ b/dom/plugins/PluginScriptableObjectParent.cpp
@@ -301,17 +301,17 @@ PluginScriptableObjectParent::Scriptable
 
   if (!actor->CallInvalidate()) {
     NS_WARNING("Failed to send message!");
   }
 
   object->invalidated = true;
 
   if (instance &&
-      !instance->CallPPluginScriptableObjectDestructor(actor)) {
+      !PPluginScriptableObjectParent::Call__delete__(actor)) {
     NS_WARNING("Failed to send message!");
   }
 }
 
 // static
 void
 PluginScriptableObjectParent::ScriptableDeallocate(NPObject* aObject)
 {
--- a/dom/plugins/PluginStreamChild.cpp
+++ b/dom/plugins/PluginStreamChild.cpp
@@ -36,43 +36,60 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "PluginStreamChild.h"
 #include "mozilla/plugins/PluginInstanceChild.h"
 
 namespace mozilla {
 namespace plugins {
 
-PluginStreamChild::PluginStreamChild(PluginInstanceChild* instance)
-  : mInstance(instance)
+PluginStreamChild::PluginStreamChild()
 {
   memset(&mStream, 0, sizeof(mStream));
   mStream.ndata = static_cast<AStream*>(this);
 }
 
+bool
+PluginStreamChild::Answer__delete__(const NPReason& reason,
+                                    const bool& artificial)
+{
+  AssertPluginThread();
+  if (!artificial)
+    NPP_DestroyStream(reason);
+  return true;
+}
+
 int32_t
 PluginStreamChild::NPN_Write(int32_t length, void* buffer)
 {
   AssertPluginThread();
 
   int32_t written = 0;
   CallNPN_Write(nsCString(static_cast<char*>(buffer), length),
                 &written);
   if (written < 0)
-    mInstance->CallPPluginStreamDestructor(this, NPERR_GENERIC_ERROR, true);
+    PPluginStreamChild::Call__delete__(this, NPERR_GENERIC_ERROR, true);
+  // careful after here! |this| just got deleted 
 
   return written;
 }
 
 void
 PluginStreamChild::NPP_DestroyStream(NPError reason)
 {
   AssertPluginThread();
 
   if (mClosed)
     return;
 
   mClosed = true;
-  mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream, reason);
+  Instance()->mPluginIface->destroystream(
+    &Instance()->mData, &mStream, reason);
+}
+
+PluginInstanceChild*
+PluginStreamChild::Instance()
+{
+  return static_cast<PluginInstanceChild*>(Manager());
 }
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/PluginStreamChild.h
+++ b/dom/plugins/PluginStreamChild.h
@@ -46,37 +46,41 @@ namespace plugins {
 
 class PluginInstanceChild;
 
 class PluginStreamChild : public PPluginStreamChild, public AStream
 {
   friend class PluginInstanceChild;
 
 public:
-  PluginStreamChild(PluginInstanceChild* instance);
+  PluginStreamChild();
   virtual ~PluginStreamChild() { }
 
   NS_OVERRIDE virtual bool IsBrowserStream() { return false; }
 
+  virtual bool Answer__delete__(const NPReason& reason,
+                                const bool& artificial);
+
   int32_t NPN_Write(int32_t length, void* buffer);
   void NPP_DestroyStream(NPError reason);
 
   void EnsureCorrectInstance(PluginInstanceChild* i)
   {
-    if (i != mInstance)
+    if (i != Instance())
       NS_RUNTIMEABORT("Incorrect stream instance");
   }
   void EnsureCorrectStream(NPStream* s)
   {
     if (s != &mStream)
       NS_RUNTIMEABORT("Incorrect stream data");
   }
 
 private:
-  PluginInstanceChild* mInstance;
+  PluginInstanceChild* Instance();
+
   NPStream mStream;
   bool mClosed;
 };
 
 
 } // namespace plugins
 } // namespace mozilla
 
--- a/dom/plugins/PluginStreamParent.cpp
+++ b/dom/plugins/PluginStreamParent.cpp
@@ -70,16 +70,25 @@ PluginStreamParent::AnswerNPN_Write(cons
                                          data.Length(),
                                          const_cast<char*>(data.get()));
   if (*written < 0)
     mClosed = true;
 
   return true;
 }
 
+bool
+PluginStreamParent::Answer__delete__(const NPError& reason,
+                                     const bool& artificial)
+{
+  if (!artificial)
+    this->NPN_DestroyStream(reason);
+  return true;
+}
+
 void
 PluginStreamParent::NPN_DestroyStream(NPReason reason)
 {
   if (mClosed)
     return;
 
   mInstance->mNPNIface->destroystream(mInstance->mNPP, mStream, reason);
   mClosed = true;
--- a/dom/plugins/PluginStreamParent.h
+++ b/dom/plugins/PluginStreamParent.h
@@ -55,19 +55,21 @@ public:
   PluginStreamParent(PluginInstanceParent* npp, const nsCString& mimeType,
                      const nsCString& target, NPError* result);
   virtual ~PluginStreamParent() { }
 
   NS_OVERRIDE virtual bool IsBrowserStream() { return false; }
 
   virtual bool AnswerNPN_Write(const Buffer& data, int32_t* written);
 
+  virtual bool Answer__delete__(const NPError& reason, const bool& artificial);
+
+private:
   void NPN_DestroyStream(NPReason reason);
 
-private:
   PluginInstanceParent* mInstance;
   NPStream* mStream;
   bool mClosed;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
--- a/dom/plugins/StreamNotifyChild.h
+++ b/dom/plugins/StreamNotifyChild.h
@@ -54,16 +54,18 @@ public:
     : mURL(aURL)
     , mClosure(NULL)
   { }
 
   void SetValid(void* aClosure) {
     mClosure = aClosure;
   }
 
+  bool Answer__delete__(const NPReason& reason);
+
 private:
   nsCString mURL;
   void* mClosure;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -291,33 +291,39 @@ class MessageDecl(Node):
         self.inParams += inParamsList
 
     def addOutParams(self, outParamsList):
         self.outParams += outParamsList
 
     def hasReply(self):
         return self.sendSemantics is SYNC or self.sendSemantics is RPC
 
-class TransitionStmt(Node):
-    def __init__(self, loc, state, transitions):
-        Node.__init__(self, loc)
-        self.state = state
-        self.transitions = transitions
-
 class Transition(Node):
     def __init__(self, loc, trigger, msg, toStates):
         Node.__init__(self, loc)
         self.trigger = trigger
         self.msg = msg
         self.toStates = toStates
 
     @staticmethod
     def nameToTrigger(name):
         return { 'send': SEND, 'recv': RECV, 'call': CALL, 'answer': ANSWER }[name]
 
+Transition.NULL = Transition(Loc.NONE, None, None, [ ])
+
+class TransitionStmt(Node):
+    def __init__(self, loc, state, transitions):
+        Node.__init__(self, loc)
+        self.state = state
+        self.transitions = transitions
+
+    @staticmethod
+    def makeNullStmt(state):
+        return TransitionStmt(Loc.NONE, state, [ Transition.NULL ])
+
 class SEND:
     pretty = 'send'
     @classmethod
     def __hash__(cls): return hash(cls.pretty)
     @classmethod
     def direction(cls): return OUT
 class RECV:
     pretty = 'recv'
@@ -350,17 +356,17 @@ class State(Node):
     def __hash__(self):
         return hash(repr(self))
     def __ne__(self, o):
         return not (self == o)
     def __repr__(self): return '<State %r start=%r>'% (self.name, self.start)
     def __str__(self): return '<State %s start=%s>'% (self.name, self.start)
 
 State.ANY = State(Loc.NONE, '[any]', start=True)
-State.NONE = State(Loc.NONE, '[none]', start=False)
+State.DEAD = State(Loc.NONE, '[dead]', start=False)
 
 class Param(Node):
     def __init__(self, loc, typespec, name):
         Node.__init__(self, loc)
         self.name = name
         self.typespec = typespec
 
 class TypeSpec(Node):
--- a/ipc/ipdl/ipdl/builtin.py
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -51,16 +51,29 @@ Types = (
     'uint16_t',
     'int32_t',
     'uint32_t',
     'int64_t',
     'uint64_t',
     'intptr_t',
     'uintptr_t',
 
+    # NSPR types
+    'PRBool',
+    'PRPackedBool'
+    'PRInt8',
+    'PRUint8',
+    'PRInt16',
+    'PRUint16',
+    'PRInt32',
+    'PRUint32',
+    'PRInt64',
+    'PRUint64',
+    'PRSize',
+
     # Mozilla types: "less" standard things we know how serialize/deserialize
     'nsresult',
     'nsString',
     'nsCString',
 )
 
 
 Includes = (
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -463,22 +463,22 @@ class MethodDecl(Node):
         self.ret = ret                  # Type or None
         self.virtual = virtual          # bool
         self.const = const              # bool
         self.pure = pure                # bool
         self.static = static            # bool
         self.typeop = typeop            # Type or None
 
     def __deepcopy__(self, memo):
-        return MethodDecl(self.name,
-                          copy.deepcopy(self.params, memo),
-                          copy.deepcopy(self.ret, memo),
-                          self.virtual,
-                          self.const,
-                          self.pure)
+        return MethodDecl(
+            self.name,
+            copy.deepcopy(self.params, memo),
+            copy.deepcopy(self.ret, memo),
+            self.virtual, self.const, self.pure, self.static,
+            copy.deepcopy(self.typeop, memo))
 
 class MethodDefn(Block):
     def __init__(self, decl):
         Block.__init__(self)
         self.decl = decl
 
 class ConstructorDecl(MethodDecl):
     def __init__(self, name, params=[ ], explicit=0):
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -382,16 +382,23 @@ class _Result:
 def errfnSend(msg, errcode=ExprLiteral.FALSE):
     return [
         _fatalError(msg),
         StmtReturn(errcode)
     ]
 
 def errfnSendCtor(msg):  return errfnSend(msg, errcode=ExprLiteral.NULL)
 
+# TODO should this error handling be strengthened for dtors?
+def errfnSendDtor(msg):
+    return [
+        _printErrorMessage(msg),
+        StmtReturn(ExprLiteral.FALSE)
+    ]
+
 # used in |OnMessage*()| handlers that hand in-messages off to Recv*()
 # interface methods
 def errfnRecv(msg, errcode=_Result.ValuError):
     return [
         _fatalError(msg),
         StmtReturn(errcode)
     ]
 
@@ -420,16 +427,21 @@ class _ConvertToCxxType(TypeVisitor):
     def visitShmemType(self, s):
         return Type(s.name())
 
     def visitProtocolType(self, p): assert 0
     def visitMessageType(self, m): assert 0
     def visitVoidType(self, v): assert 0
     def visitStateType(self, st): assert 0
 
+def _allocMethod(ptype):
+    return ExprVar('Alloc'+ ptype.name())
+
+def _deallocMethod(ptype):
+    return ExprVar('Dealloc'+ ptype.name())
 
 class _ConvertToSerializableCxxType(TypeVisitor):
     def visitBuiltinCxxType(self, t):
         return Type(t.name())
 
     def visitImportedCxxType(self, t):
         return Type(t.name())
 
@@ -1081,71 +1093,58 @@ IPDL union type."""
         elif it.isActor():
             return _HybridDecl.bareType(self, otherside)
         else: assert 0
 
 ##--------------------------------------------------
 
 class MessageDecl(ipdl.ast.MessageDecl):
     def baseName(self):
-        if self.decl.type.isDtor():
-            return self.name[1:]
         return self.name
     
     def recvMethod(self):
         name = _recvPrefix(self.decl.type) + self.baseName()
         if self.decl.type.isCtor():
             name += 'Constructor'
-        elif self.decl.type.isDtor():
-            name += 'Destructor'
         return ExprVar(name)
 
     def sendMethod(self):
         name = _sendPrefix(self.decl.type) + self.baseName()
         if self.decl.type.isCtor():
             name += 'Constructor'
-        elif self.decl.type.isDtor():
-            name += 'Destructor'
         return ExprVar(name)
 
     def hasReply(self):
         return (self.decl.type.hasReply()
                 or self.decl.type.isCtor()
                 or self.decl.type.isDtor())
 
-    # XXX best place for this?
-    def allocMethod(self):
-        return ExprVar('Alloc'+ self.baseName())
-
-    def deallocMethod(self):
-        return ExprVar('Dealloc'+ self.baseName())
-
     def msgClass(self):
         return 'Msg_%s'% (self.decl.progname)
 
     def pqMsgClass(self):
         return '%s::%s'% (self.namespace, self.msgClass())
 
     def msgCast(self, msgexpr):
         return ExprCast(msgexpr, Type(self.pqMsgClass(), const=1, ptr=1),
-                        reinterpret=1)
+                        static=1)
 
     def msgId(self):  return self.msgClass()+ '__ID'
     def pqMsgId(self):
         return '%s::%s'% (self.namespace, self.msgId())
 
     def replyClass(self):
         return 'Reply_%s'% (self.decl.progname)
 
     def pqReplyClass(self):
         return '%s::%s'% (self.namespace, self.replyClass())
 
     def replyCast(self, replyexpr):
         return ExprCast(replyexpr, Type(self.pqReplyClass(), const=1, ptr=1),
-                        reinterpret=1)
+                        static=1)
 
     def replyId(self):  return self.replyClass()+ '__ID'
     def pqReplyId(self):
         return '%s::%s'% (self.namespace, self.replyId())
 
     def actorDecl(self):
         return self.params[0]
 
@@ -1257,31 +1256,46 @@ class Protocol(ipdl.ast.Protocol):
         return '/'.join(_semsToChannelParts(self.sendSems())) +'.h'
 
     def listenerName(self):
         return _semsToListener(self.sendSems())
 
     def fqListenerName(self):
         return self.channelName() +'::'+ _semsToListener(self.sendSems())
 
-    def managerCxxType(self, ptr=0):
+    def managerInterfaceType(self, ptr=0):
         return Type('mozilla::ipc::IProtocolManager',
                     ptr=ptr,
                     T=Type(self.fqListenerName()))
 
+    def managerActorType(self, side, ptr=0):
+        return Type(_actorName(self.decl.type.manager.name(), side),
+                    ptr=ptr)
+
+    def managerMethod(self, actorThis=None):
+        if actorThis is not None:
+            return ExprSelect(actorThis, '->', 'Manager')
+        return ExprVar('Manager');
+
+    # FIXME/bug 525181: implement
+    def stateMethod(self):
+        return ExprVar('state');
+
     def registerMethod(self):
         return ExprVar('Register')
 
     def registerIDMethod(self):
         return ExprVar('RegisterID')
 
     def lookupIDMethod(self):
         return ExprVar('Lookup')
 
-    def unregisterMethod(self):
+    def unregisterMethod(self, actorThis=None):
+        if actorThis is not None:
+            return ExprSelect(actorThis, '->', 'Unregister')
         return ExprVar('Unregister')
 
     def otherProcessMethod(self):
         return ExprVar('OtherProcess')
 
     def nextActorIdExpr(self, side):
         assert self.decl.type.isToplevel()
         if side is 'parent':   op = '++'
@@ -1301,27 +1315,31 @@ class Protocol(ipdl.ast.Protocol):
     def lastActorIdVar(self):
         assert self.decl.type.isToplevel()
         return ExprVar('mLastRouteId')
 
     def actorMapVar(self):
         assert self.decl.type.isToplevel()
         return ExprVar('mActorMap')
 
-    def channelVar(self):
+    def channelVar(self, actorThis=None):
+        if actorThis is not None:
+            return ExprSelect(actorThis, '->', 'mChannel')
         return ExprVar('mChannel')
 
     def channelForSubactor(self):
         if self.decl.type.isToplevel():
             return ExprAddrOf(self.channelVar())
         return self.channelVar()
 
-    def routingId(self):
+    def routingId(self, actorThis=None):
         if self.decl.type.isToplevel():
             return ExprVar('MSG_ROUTING_CONTROL')
+        if actorThis is not None:
+            return ExprSelect(actorThis, '->', self.idVar().name)
         return self.idVar()
 
     def idVar(self):
         assert not self.decl.type.isToplevel()
         return ExprVar('mId')
 
     def managerVar(self):
         assert not self.decl.type.isToplevel()
@@ -1387,18 +1405,19 @@ relevant to C++ code generation.
 This pass results in an AST that is a poor man's "IR"; in reality, a
 "hybrid" AST mainly consisting of IPDL nodes with new C++ info along
 with some new IPDL/C++ nodes that are tuned for C++ codegen."""
 
     def __init__(self):
         # the set of typedefs that allow generated classes to
         # reference known C++ types by their "short name" rather than
         # fully-qualified name. e.g. |Foo| rather than |a::b::Foo|.
-        self.typedefs = [ Typedef(Type('mozilla::ipc::ActorHandle'),
-                                  'ActorHandle') ]
+        self.typedefs = [ 
+            Typedef(Type('mozilla::ipc::ActorHandle'), 'ActorHandle')
+        ]
         self.protocolName = None
 
     def visitProtocol(self, pro):
         self.protocolName = pro.name
         pro.decl.cxxtypedefs = self.typedefs
         Protocol.upgrade(pro)
         return ipdl.ast.Visitor.visitProtocol(self, pro)
 
@@ -1421,17 +1440,17 @@ with some new IPDL/C++ nodes that are tu
                 newcomponents.append(_UnionMember(ctype, ud, side='child'))
             else:
                 newcomponents.append(_UnionMember(ctype, ud))
         ud.components = newcomponents
         UnionDecl.upgrade(ud)
 
         if ud.decl.fullname is not None:
             self.typedefs.append(Typedef(Type(ud.fqClassName()), ud.name))
-            
+
 
     def visitDecl(self, decl):
         return _HybridDecl(decl.type, decl.progname)
 
     def visitMessageDecl(self, md):
         md.namespace = self.protocolName
         md.params = [ param.accept(self) for param in md.inParams ]
         md.returns = [ ret.accept(self) for ret in md.outParams ]
@@ -2432,23 +2451,26 @@ class _GenerateProtocolActorCode(ipdl.as
             self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
 
         self.hdrfile.addthings([
             CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
             Whitespace.NL ])
 
         inherits = [ Inherit(Type(p.fqListenerName())) ]
         if p.decl.type.isManager():
-            inherits.append(Inherit(p.managerCxxType()))
+            inherits.append(Inherit(p.managerInterfaceType()))
         self.cls = Class(self.clsname, inherits=inherits, abstract=True)
 
         friends = _FindFriends().findFriends(p.decl.type)
         if p.decl.type.isManaged():
             friends.add(p.decl.type.manager)
 
+        # |friend| managed actors so that they can call our Dealloc*()
+        friends.update(p.decl.type.manages)
+
         for friend in friends:
             self.hdrfile.addthings([
                 Whitespace.NL,
                 _makeForwardDecl(friend, self.prettyside),
                 Whitespace.NL
             ])
             self.cls.addstmts([
                 FriendClassDecl(_actorName(friend.fullname(),
@@ -2460,49 +2482,55 @@ class _GenerateProtocolActorCode(ipdl.as
             self.cls.addstmt(typedef)
         for typedef in self.includedActorTypedefs:
             self.cls.addstmt(typedef)
         self.cls.addstmt(Whitespace.NL)
 
         # interface methods that the concrete subclass has to impl
         for md in p.messageDecls:
             isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor()
-            if isctor:
-                self.cls.addstmt(StmtDecl(MethodDecl(
-                    md.allocMethod().name,
-                    params=md.makeCxxParams(paramsems='in', returnsems='out',
-                                            side=self.side,
-                                            implicit=0),
-                    ret=md.actorDecl().bareType(self.side),
-                    virtual=1, pure=1)))
-            elif isdtor:
-                self.cls.addstmt(StmtDecl(MethodDecl(
-                    md.deallocMethod().name,
-                    params=md.makeCxxParams(paramsems='in', returnsems='out',
-                                            side=self.side,
-                                            implicit=1),
-                    ret=Type.BOOL,
-                    virtual=1, pure=1)))
 
             if self.receivesMessage(md):
                 # generate Recv/Answer* interface
+                implicit = (not isdtor)
                 recvDecl = MethodDecl(
                     md.recvMethod().name,
                     params=md.makeCxxParams(paramsems='in', returnsems='out',
-                                            side=self.side, implicit=1),
+                                            side=self.side, implicit=implicit),
                     ret=Type.BOOL, virtual=1)
 
                 if isctor or isdtor:
                     defaultRecv = MethodDefn(recvDecl)
                     defaultRecv.addstmt(StmtReturn(ExprLiteral.TRUE))
                     self.cls.addstmt(defaultRecv)
                 else:
                     recvDecl.pure = 1
                     self.cls.addstmt(StmtDecl(recvDecl))
 
+        for md in p.messageDecls:
+            managed = md.decl.type.constructedType()
+            if not p.decl.type.isManagerOf(managed):
+                continue
+
+            # add the Alloc/Dealloc interface for managed actors
+            actortype = md.actorDecl().bareType(self.side)
+            
+            self.cls.addstmt(StmtDecl(MethodDecl(
+                _allocMethod(managed).name,
+                params=md.makeCxxParams(side=self.side, implicit=0),
+                ret=actortype,
+                virtual=1, pure=1)))
+
+            self.cls.addstmt(StmtDecl(MethodDecl(
+                _deallocMethod(managed).name,
+                params=[ Decl(actortype, 'actor') ],
+                ret=Type.BOOL,
+                virtual=1, pure=1)))
+
+
         self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmts((
             [ Label.PRIVATE ]
             + self.standardTypedefs()
             + [ Whitespace.NL ]
         ))
 
@@ -2559,16 +2587,25 @@ class _GenerateProtocolActorCode(ipdl.as
                 Whitespace.NL ])
 
             # Close()
             closemeth = MethodDefn(MethodDecl('Close'))
             closemeth.addstmt(StmtExpr(
                 ExprCall(ExprSelect(p.channelVar(), '.', 'Close'))))
             self.cls.addstmts([ closemeth, Whitespace.NL ])
 
+        if not p.decl.type.isToplevel():
+            ## manager()
+            managertype = p.managerActorType(self.side, ptr=1)
+            managermeth = MethodDefn(MethodDecl(
+                p.managerMethod().name, ret=managertype))
+            managermeth.addstmt(StmtReturn(p.managerVar()))
+
+            self.cls.addstmts([ managermeth, Whitespace.NL ])
+
         ## OnMessageReceived()/OnCallReceived()
 
         # save these away for use in message handler case stmts
         msgvar = ExprVar('msg')
         self.msgvar = msgvar
         replyvar = ExprVar('reply')
         self.replyvar = replyvar
         
@@ -2713,17 +2750,18 @@ class _GenerateProtocolActorCode(ipdl.as
                               p.actorMapVar().name)),
                 StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name)),
                 StmtDecl(Decl(Type('ProcessHandle'),
                               p.otherProcessVar().name))
             ])
         elif p.decl.type.isManaged():
             self.cls.addstmts([
                 StmtDecl(Decl(_actorIdType(), p.idVar().name)),
-                StmtDecl(Decl(p.managerCxxType(ptr=1), p.managerVar().name))
+                StmtDecl(Decl(p.managerActorType(self.side, ptr=1),
+                              p.managerVar().name))
             ])
         if p.usesShmem():
             self.cls.addstmts([
                 StmtDecl(Decl(Type('IDMap', T=_rawShmemType()),
                               p.shmemMapVar().name)),
                 StmtDecl(Decl(_shmemIdType(), p.lastShmemIdVar().name))
             ])
 
@@ -3009,29 +3047,39 @@ class _GenerateProtocolActorCode(ipdl.as
             elif isdtor and isasync:
                 sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md)
             elif isdtor:
                 sendmethod = self.genBlockingDtorMethod(md)
             elif isasync:
                 sendmethod = self.genAsyncSendMethod(md)
             else:
                 sendmethod = self.genBlockingSendMethod(md)
+
+        # XXX figure out what to do here
+        if isdtor and md.decl.type.constructedType().isToplevel():
+            sendmethod = None
+                
         if sendmethod is not None:
             self.cls.addstmts([ sendmethod, Whitespace.NL ])
         if recvcase is not None:
             addRecvCase(recvlbl, recvcase)
             recvlbl, recvcase = None, None
 
         if self.receivesMessage(md):
             if isctor:
                 recvlbl, recvcase = self.genCtorRecvCase(md)
             elif isdtor:
                 recvlbl, recvcase = self.genDtorRecvCase(md)
             else:
                 recvlbl, recvcase = self.genRecvCase(md)
+
+            # XXX figure out what to do here
+            if isdtor and md.decl.type.constructedType().isToplevel():
+                return
+
             addRecvCase(recvlbl, recvcase)
 
 
     def genAsyncCtor(self, md):
         actor = md.actorDecl()
         method = MethodDefn(self.makeSendMethodDecl(md))
         method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])
 
@@ -3112,90 +3160,93 @@ class _GenerateProtocolActorCode(ipdl.as
         callctor = self.callAllocActor(md, retsems='out')
         helper.addstmt(StmtReturn(ExprCall(
             ExprVar(helperdecl.name), args=[ callctor ] + callctor.args)))
         return helper
 
 
     def genAsyncDtor(self, md):
         actor = md.actorDecl()
-        method = MethodDefn(self.makeSendMethodDecl(md))
+        actorvar = actor.var()
+        method = MethodDefn(self.makeDtorMethodDecl(md))
 
         method.addstmts(self.dtorPrologue(actor.var()))
-
-        msgvar, stmts = self.makeMessage(md, errfnSend)
-        sendok, sendstmts = self.sendAsync(md, msgvar)
+        method.addstmts(self.dtorPrologue(actorvar))
+
+        msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
+        sendok, sendstmts = self.sendAsync(md, msgvar, actorvar)
         method.addstmts(
             stmts
             + sendstmts
             + [ Whitespace.NL ]
-            + self.dtorEpilogue(md, actor.var(), retsems='out')
+            + self.dtorEpilogue(md, actor.var())
             + [ StmtReturn(sendok) ])
 
         lbl = CaseLabel(md.pqReplyId())
         case = StmtBlock()
         case.addstmt(StmtReturn(_Result.Processed))
         # TODO if the dtor is "inherently racy", keep the actor alive
         # until the other side acks
 
         return method, (lbl, case)
 
 
     def genBlockingDtorMethod(self, md):
         actor = md.actorDecl()
-        method = MethodDefn(self.makeSendMethodDecl(md))
-
-        method.addstmts(self.dtorPrologue(actor.var()))
-
-        msgvar, stmts = self.makeMessage(md, errfnSend)
+        actorvar = actor.var()
+        method = MethodDefn(self.makeDtorMethodDecl(md))
+
+        method.addstmts(self.dtorPrologue(actorvar))
+
+        msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
 
         replyvar = self.replyvar
-        sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
+        sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar, actorvar)
         method.addstmts(
             stmts
             + [ Whitespace.NL,
                 StmtDecl(Decl(Type('Message'), replyvar.name)) ]
             + sendstmts)
 
         readok, destmts = self.deserializeReply(
             md, ExprAddrOf(replyvar), self.side, errfnSend)
         ifsendok = StmtIf(sendok)
         ifsendok.addifstmts(destmts)
         ifsendok.addifstmts([ Whitespace.NL,
                               StmtExpr(ExprAssn(sendok, readok, '&=')) ])
 
         method.addstmts(
             [ ifsendok ]
-            + self.dtorEpilogue(md, actor.var(), retsems='out')
+            + self.dtorEpilogue(md, actor.var())
             + [ Whitespace.NL, StmtReturn(sendok) ])
 
         return method
 
     def dtorPrologue(self, actorexpr):
         return [ self.failIfNullActor(actorexpr), Whitespace.NL ]
 
-    def dtorEpilogue(self, md, actorexpr, retsems):
+    def dtorEpilogue(self, md, actorexpr):
         return (self.unregisterActor(actorexpr)
-                + [ StmtExpr(self.callDeallocActor(md, retsems)) ])
+                + [ StmtExpr(self.callDeallocActor(md, actorexpr)) ])
 
     def genAsyncSendMethod(self, md):
         method = MethodDefn(self.makeSendMethodDecl(md))
         msgvar, stmts = self.makeMessage(md, errfnSend)
         sendok, sendstmts = self.sendAsync(md, msgvar)
         method.addstmts(stmts
                         +[ Whitespace.NL ]
                         + sendstmts
                         +[ StmtReturn(sendok) ])
         return method
 
 
-    def genBlockingSendMethod(self, md):
+    def genBlockingSendMethod(self, md, fromActor=None):
         method = MethodDefn(self.makeSendMethodDecl(md))
 
-        msgvar, serstmts = self.makeMessage(md, errfnSend)
+        msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor)
         replyvar = self.replyvar
 
         sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
         failif = StmtIf(ExprNot(sendok))
         failif.addifstmt(StmtReturn(ExprLiteral.FALSE))
 
         readok, desstmts = self.deserializeReply(
             md, ExprAddrOf(replyvar), self.side, errfnSend)
@@ -3251,19 +3302,19 @@ class _GenerateProtocolActorCode(ipdl.as
         failif = StmtIf(ExprNot(readok))
         failif.addifstmt(StmtReturn(_Result.PayloadError))
 
         case.addstmts(
             stmts
             + [ failif, Whitespace.NL ]
             + [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
                 for r in md.returns ]
-            + self.invokeRecvHandler(md)
+            + self.invokeRecvHandler(md, implicit=0)
             + [ Whitespace.NL ]
-            + self.dtorEpilogue(md, md.actorDecl().var(), retsems='in')
+            + self.dtorEpilogue(md, md.actorDecl().var())
             + self.makeReply(md, errfnRecv)
             + [ Whitespace.NL,
                 StmtReturn(_Result.Processed) ])
         
         return lbl, case
 
 
     def genRecvCase(self, md):
@@ -3290,36 +3341,36 @@ class _GenerateProtocolActorCode(ipdl.as
     # helper methods
 
     def failIfNullActor(self, actorExpr, retOnNull=ExprLiteral.FALSE):
         failif = StmtIf(ExprNot(actorExpr))
         failif.addifstmt(StmtReturn(retOnNull))
         return failif
 
     def unregisterActor(self, actorexpr):
-        return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(),
+        return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(actorexpr),
                                    args=[ _actorId(actorexpr) ])),
                  StmtExpr(ExprAssn(_actorId(actorexpr), _FREED_ACTOR_ID)) ]
 
-    def makeMessage(self, md, errfn):
+    def makeMessage(self, md, errfn, fromActor=None):
         msgvar = self.msgvar
         stmts = [ StmtDecl(Decl(Type(md.pqMsgClass(), ptr=1), msgvar.name)),
                   Whitespace.NL ]
         msgCtorArgs = [ ]
 
         for param in md.params:
             arg, sstmts = param.serialize(param.var(), self.side, errfn)
             msgCtorArgs.append(arg)
             stmts.extend(sstmts)
 
         stmts.extend([
             StmtExpr(ExprAssn(
                 msgvar,
                 ExprNew(Type(md.pqMsgClass()), args=msgCtorArgs))) ]
-            + self.setMessageFlags(md, msgvar, reply=0))
+            + self.setMessageFlags(md, msgvar, reply=0, actor=fromActor))
 
         return msgvar, stmts
 
 
     def makeReply(self, md, errfn):
         # TODO special cases for async ctor/dtor replies
         if md.decl.type.isAsync():
             return [ ]
@@ -3337,20 +3388,20 @@ class _GenerateProtocolActorCode(ipdl.as
                 replyvar,
                 ExprNew(Type(md.pqReplyClass()), args=replyCtorArgs))) ]
             + self.setMessageFlags(md, replyvar, reply=1)
             +[ self.logMessage(md, md.replyCast(replyvar), 'Sending reply ') ])
         
         return stmts
 
 
-    def setMessageFlags(self, md, var, reply):
+    def setMessageFlags(self, md, var, reply, actor=None):
         stmts = [ StmtExpr(ExprCall(
             ExprSelect(var, '->', 'set_routing_id'),
-            args=[ self.protocol.routingId() ])) ]
+            args=[ self.protocol.routingId(actor) ])) ]
 
         if md.decl.type.isSync():
             stmts.append(StmtExpr(ExprCall(
                 ExprSelect(var, '->', 'set_sync'))))
         elif md.decl.type.isRpc():
             stmts.append(StmtExpr(ExprCall(
                 ExprSelect(var, '->', 'set_rpc'))))
 
@@ -3431,65 +3482,72 @@ class _GenerateProtocolActorCode(ipdl.as
                 ExprDeref(readexprs[i]), side, sems='out',
                 errfn=errfn))
         if len(ifok.ifb.stmts):
             stmts.extend([ Whitespace.NL, ifok ])
 
         return okvar, stmts
 
 
-    def sendAsync(self, md, msgexpr):
+    def sendAsync(self, md, msgexpr, actor=None):
         sendok = ExprVar('__sendok')
         return (
             sendok,
             [ Whitespace.NL,
               self.logMessage(md, msgexpr, 'Sending '),
               Whitespace.NL,
               StmtDecl(Decl(Type.BOOL, sendok.name),
                        init=ExprCall(
-                           ExprSelect(self.protocol.channelVar(),
+                           ExprSelect(self.protocol.channelVar(actor),
                                       self.protocol.channelSel(), 'Send'),
                            args=[ msgexpr ]))
             ])
 
-    def sendBlocking(self, md, msgexpr, replyexpr):
+    def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
         sendok = ExprVar('__sendok')
         return (
             sendok,
             [ Whitespace.NL,
               self.logMessage(md, msgexpr, 'Sending '),
               Whitespace.NL,
-              StmtDecl(Decl(Type.BOOL, sendok.name),
-                       init=ExprCall(ExprSelect(self.protocol.channelVar(),
-                                                self.protocol.channelSel(),
-                                                _sendPrefix(md.decl.type)),
-                                     args=[ msgexpr,
-                                            ExprAddrOf(replyexpr) ]))
+              StmtDecl(
+                  Decl(Type.BOOL, sendok.name),
+                  init=ExprCall(ExprSelect(self.protocol.channelVar(actor),
+                                           self.protocol.channelSel(),
+                                           _sendPrefix(md.decl.type)),
+                                args=[ msgexpr, ExprAddrOf(replyexpr) ]))
             ])
-                                            
 
     def callAllocActor(self, md, retsems):
         return ExprCall(
-            md.allocMethod(),
+            _allocMethod(md.decl.type.constructedType()),
             args=md.makeCxxArgs(params=1, retsems=retsems, retcallsems='out',
                                 implicit=0))
 
-    def callDeallocActor(self, md, retsems):
+    def callDeallocActor(self, md, actorexpr):
+        actor = md.decl.type.constructedType()
         return ExprCall(
-            md.deallocMethod(),
-            args=md.makeCxxArgs(params=1, retsems=retsems, retcallsems='out'))
-
-    def invokeRecvHandler(self, md):
+            ExprSelect(ExprCall(self.protocol.managerMethod(actorexpr)), '->',
+                       _deallocMethod(md.decl.type.constructedType()).name),
+            args=[ actorexpr ])
+
+    def invokeRecvHandler(self, md, implicit=1):
         failif = StmtIf(ExprNot(
             ExprCall(md.recvMethod(),
                      args=md.makeCxxArgs(params=1,
-                                         retsems='in', retcallsems='out'))))
+                                         retsems='in', retcallsems='out',
+                                         implicit=implicit))))
         failif.addifstmt(StmtReturn(_Result.ValuError))
         return [ failif ]
 
+    def makeDtorMethodDecl(self, md):
+        decl = self.makeSendMethodDecl(md)
+        decl.static = 1
+        return decl
+
     def makeSendMethodDecl(self, md):
         implicit = md.decl.type.hasImplicitActorParam()
         decl = MethodDecl(
             md.sendMethod().name,
             params=md.makeCxxParams(paramsems='in', returnsems='out',
                                     side=self.side, implicit=implicit),
             ret=Type.BOOL)
         if md.decl.type.isCtor():
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -140,16 +140,18 @@ def locFromTok(p, num):
 ##-----------------------------------------------------------------------------
 
 reserved = set((
         'answer',
         'async',
         'both',
         'call',
         'child',
+        '__delete__',
+        'delete',                       # reserve 'delete' to prevent its use
         'goto',
         'include',
         'manager',
         'manages',
         'namespace',
         'nullable',
         'or',
         'parent',
@@ -390,19 +392,23 @@ def p_MessageBody(p):
     msg = MessageDecl(locFromTok(p, 1))
     msg.name = p[1]
     msg.addInParams(p[2])
     msg.addOutParams(p[3])
     p[0] = msg
 
 def p_MessageId(p):
     """MessageId : ID
+                 | __DELETE__
+                 | DELETE
                  | '~' ID"""
     if 3 == len(p):
-        p[1] += p[2] # munge dtor name to "~Foo". handled later
+        _error(locFromTok(p, 1), "sorry, `%s()' destructor syntax is a relic from a bygone era.  Declare `__delete__()' in the `%s' protocol instead", p[1]+p[2], p[2])
+    elif 'delete' == p[1]:
+        _error(locFromTok(p, 1), "`delete' is a reserved identifier")
     p[0] = p[1]
 
 def p_MessageInParams(p):
     """MessageInParams : '(' ParamList ')'"""
     p[0] = p[2]
 
 def p_MessageOutParams(p):
     """MessageOutParams : RETURNS '(' ParamList ')'
@@ -447,19 +453,28 @@ def p_Transitions(p):
                    | Transition"""
     if 3 == len(p):
         p[1].append(p[2])
         p[0] = p[1]
     else:
         p[0] = [ p[1] ]
 
 def p_Transition(p):
-    """Transition : Trigger MessageId GOTO StateList ';'"""
+    """Transition : Trigger ID GOTO StateList ';'
+                  | Trigger __DELETE__ ';'
+                  | Trigger DELETE ';'"""
+    if 'delete' == p[2]:
+        _error(locFromTok(p, 1), "`delete' is a reserved identifier")
+    
     loc, trigger = p[1]
-    p[0] = Transition(loc, trigger, p[2], p[4])
+    if 6 == len(p):
+        nextstates = p[4]
+    else:
+        nextstates = [ State.DEAD ]
+    p[0] = Transition(loc, trigger, p[2], nextstates)
 
 def p_Trigger(p):
     """Trigger : SEND
                | RECV
                | CALL
                | ANSWER"""
     p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ]
 
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -27,19 +27,20 @@
 # 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 *****
 
 import os, sys
 
-from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, TransitionStmt, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
 import ipdl.builtin as builtin
 
+_DELETE_MSG = '__delete__'
 
 class TypeVisitor:
     def defaultVisit(self, node, *args):
         raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
             node.__class__.__name__)
 
     def visitVoidType(self, v, *args):
         pass
@@ -225,21 +226,22 @@ class MessageType(IPDLType):
     def isIn(self): return self.direction is IN
     def isOut(self): return self.direction is OUT
     def isInout(self): return self.direction is INOUT
 
     def hasImplicitActorParam(self):
         return self.isCtor() or self.isDtor()
 
 class ProtocolType(IPDLType):
-    def __init__(self, qname, sendSemantics):
+    def __init__(self, qname, sendSemantics, stateless=False):
         self.qname = qname
         self.sendSemantics = sendSemantics
         self.manager = None
         self.manages = [ ]
+        self.stateless = stateless
     def isProtocol(self): return True
 
     def name(self):
         return self.qname.baseid
     def fullname(self):
         return str(self.qname)
 
     def managedBy(self, mgr):
@@ -442,17 +444,16 @@ With this information, it finally type c
 
         # now that the nodes have decls, type checking is much easier.
         if not runpass(CheckTypes(self.errors)):
             return False
 
         if (len(tu.protocol.startStates)
             and not runpass(CheckStateMachine(self.errors))):
             return False
-
         return True
 
     def reportErrors(self, errout):
         for error in self.errors:
             print >>errout, error
 
 
 class TcheckVisitor(Visitor):
@@ -509,17 +510,18 @@ class GatherDecls(TcheckVisitor):
         # that works for C++ and protocol types?
         qname = p.qname()
         if 0 == len(qname.quals):
             fullname = None
         else:
             fullname = str(qname)
         p.decl = self.declare(
             loc=p.loc,
-            type=ProtocolType(qname, p.sendSemantics),
+            type=ProtocolType(qname, p.sendSemantics,
+                              stateless=(0 == len(p.transitionStmts))),
             shortname=p.name,
             fullname=fullname)
 
         # XXX ugh, this sucks.  but we need this information to compute
         # what friend decls we need in generated C++
         p.decl.type._p = p
 
         # make sure we have decls for all dependent protocols
@@ -538,16 +540,21 @@ class GatherDecls(TcheckVisitor):
         p.accept(self)
 
         tu.type = VOID
 
         self.symtab = savedSymtab
 
 
     def visitProtocolInclude(self, pi):
+        if pi.tu is None:
+            self.error(
+                pi.loc,
+                "(type checking here will be unreliable because of an earlier error)")
+            return
         pi.tu.accept(self)
         self.symtab.declare(pi.tu.protocol.decl)
 
     def visitUnionDecl(self, ud):
         qname = ud.qname()
         if 0 == len(qname.quals):
             fullname = None
         else:
@@ -599,53 +606,69 @@ class GatherDecls(TcheckVisitor):
                        "top-level protocol `%s' cannot be empty",
                        p.name)
 
         setattr(self, 'currentProtocolDecl', p.decl)
         for msg in p.messageDecls:
             msg.accept(self)
         del self.currentProtocolDecl
 
+        if not p.decl.type.isToplevel():
+            dtordecl = self.symtab.lookup(_DELETE_MSG)
+            if not dtordecl:
+                self.error(
+                    p.loc,
+                    "destructor declaration `delete(...)' required for managed protocol `%s'",
+                    p.name)
+
         for managed in p.managesStmts:
             mgdname = managed.name
             ctordecl = self.symtab.lookup(mgdname +'Constructor')
-            dtordecl = self.symtab.lookup(mgdname +'Destructor')
 
-            if not(ctordecl and dtordecl
-                   and ctordecl.type.isCtor() and dtordecl.type.isDtor()):
+            if not (ctordecl and ctordecl.type.isCtor()):
                 self.error(
                     managed.loc,
-                    "constructor and destructor declarations are required for managed protocol `%s' (managed by protocol `%s')",
+                    "constructor declaration required for managed protocol `%s' (managed by protocol `%s')",
                     mgdname, p.name)
 
         p.states = { }
         
         if len(p.transitionStmts):
             p.startStates = [ ts for ts in p.transitionStmts
                               if ts.state.start ]
             if 0 == len(p.startStates):
                 p.startStates = [ p.transitionStmts[0] ]
+
+        # declare implicit "any" and "dead" states
+        self.declare(loc=State.ANY.loc,
+                     type=StateType(p.decl.type, State.ANY.name, start=False),
+                     progname=State.ANY.name)
+        self.declare(loc=State.DEAD.loc,
+                     type=StateType(p.decl.type, State.DEAD.name, start=False),
+                     progname=State.DEAD.name)
                 
         # declare each state before decorating their mention
         for trans in p.transitionStmts:
             p.states[trans.state] = trans
             trans.state.decl = self.declare(
                 loc=trans.state.loc,
                 type=StateType(p.decl.type, trans.state, trans.state.start),
                 progname=trans.state.name)
 
-        # declare implicit "any" state
-        self.declare(loc=State.ANY.loc,
-                     type=StateType(p.decl.type, State.ANY.name, start=False),
-                     progname=State.ANY.name)
-
         for trans in p.transitionStmts:
             self.seentriggers = set()
             trans.accept(self)
 
+        if not (p.decl.type.stateless
+                or (p.decl.type.isToplevel()
+                    and None is self.symtab.lookup(_DELETE_MSG))):
+            # add a special state |state DEAD: null goto DEAD;|
+            deadtrans = TransitionStmt.makeNullStmt(State.DEAD)
+            p.states[State.DEAD] = deadtrans           
+
         # visit the message decls once more and resolve the state names
         # attached to actor params and returns
         def resolvestate(loc, actortype):
             assert actortype.isIPDL() and actortype.isActor()
 
             # already resolved this guy's state
             if isinstance(actortype.state, Decl):
                 return
@@ -745,43 +768,31 @@ class GatherDecls(TcheckVisitor):
     def visitMessageDecl(self, md):
         msgname = md.name
         loc = md.loc
 
         isctor = False
         isdtor = False
         cdtype = None
 
-        if '~' == msgname[0]:
-            # it's a destructor.  look up the constructed type
-            msgname = msgname[1:]
-
-            decl = self.symtab.lookup(msgname)
-            if decl is None:
-                self.error(loc, "type `%s' has not been declared", msgname)
-            elif not decl.type.isProtocol():
-                self.error(loc, "destructor for non-protocol type `%s'",
-                           msgname)
-            else:
-                msgname += 'Destructor'
-                isdtor = True
-                cdtype = decl.type
-
         decl = self.symtab.lookup(msgname)
-
         if decl is not None and decl.type.isProtocol():
             # probably a ctor.  we'll check validity later.
             msgname += 'Constructor'
             isctor = True
             cdtype = decl.type
         elif decl is not None:
             self.error(loc, "message name `%s' already declared as `%s'",
                        msgname, decl.type.typename())
             # if we error here, no big deal; move on to find more
-        decl = None
+
+        if _DELETE_MSG == msgname:
+            isdtor = True
+            cdtype = self.currentProtocolDecl.type
+
 
         # enter message scope
         self.symtab.enterScope(md)
 
         msgtype = MessageType(md.sendSemantics, md.direction,
                               ctor=isctor, dtor=isdtor, cdtype=cdtype)
 
         # replace inparam Param nodes with proper Decls
@@ -933,16 +944,27 @@ class CheckTypes(TcheckVisitor):
         mgrtype = ptype.manager
         if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
             mgrname = p.manager.decl.shortname
             self.error(
                 p.decl.loc,
                 "protocol `%s' requires more powerful send semantics than its manager `%s' provides",
                 pname, mgrname)
 
+        # XXX currently we don't require a delete() message of top-level
+        # actors.  need to let experience guide this decision
+        if not p.decl.type.isToplevel():
+            for md in p.messageDecls:
+                if _DELETE_MSG == md.name: break
+            else:
+                self.error(
+                    p.decl.loc,
+                   "managed protocol `%s' requires a `delete()' message to be declared",
+                    p.name)
+
         return Visitor.visitProtocol(self, p)
         
 
     def visitManagesStmt(self, mgs):
         pdecl = mgs.manager.decl
         ptype, pname = pdecl.type, pdecl.shortname
 
         mgsdecl = mgs.decl
@@ -1004,30 +1026,30 @@ class CheckTypes(TcheckVisitor):
                 mname, pname)
 
         if mtype.isAsync() and len(mtype.returns):
             # XXX/cjones could modify grammar to disallow this ...
             self.error(loc,
                        "asynchronous message `%s' declares return values",
                        mname)
 
-        if (mtype.isCtor() or mtype.isDtor()) and not ptype.isManagerOf(mtype.constructedType()):
+        if mtype.isCtor() and not ptype.isManagerOf(mtype.constructedType()):
             self.error(
                 loc,
-                "ctor/dtor for protocol `%s', which is not managed by protocol `%s'", 
+                "ctor for protocol `%s', which is not managed by protocol `%s'", 
                 mname[:-len('constructor')], pname)
 
 
     def visitTransition(self, t):
         _YNC = [ ASYNC, SYNC ]
 
         loc = t.loc
         impliedDirection, impliedSems = {
             SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ],
-            CALL: [ OUT, RPC ],  ANSWER: [ IN, RPC ]
+            CALL: [ OUT, RPC ],  ANSWER: [ IN, RPC ],
          } [t.trigger]
         
         if (OUT is impliedDirection and t.msg.type.isIn()
             or IN is impliedDirection and t.msg.type.isOut()
             or _YNC is impliedSems and t.msg.type.isRpc()
             or RPC is impliedSems and (not t.msg.type.isRpc())):
             mtype = t.msg.type
 
@@ -1248,23 +1270,35 @@ direction as trigger |t|'''
                     # violation per state. there may be O(n^4)
                     # total, way too many for a human to parse
                     #
                     # XXX/cjones: could set a limit on #printed
                     # and stop after that limit ...
                     return
 
     def checkReachability(self, p):
-        visited = set()         # set(State)
-        def explore(ts):
+        def explore(ts, visited):
             if ts.state in visited:
                 return
             visited.add(ts.state)
             for outedge in ts.transitions:
                 for toState in outedge.toStates:
-                    explore(p.states[toState])
+                    explore(p.states[toState], visited)
 
+        checkfordelete = (State.DEAD in p.states)
+
+        allvisited = set()         # set(State)
         for root in p.startStates:
-            explore(root)
-        for ts in p.transitionStmts:
-            if ts.state not in visited:
-                self.error(ts.loc, "unreachable state `%s' in protocol `%s'",
+            visited = set()
+
+            explore(root, visited)
+            allvisited.update(visited)
+
+            if checkfordelete and State.DEAD not in visited:
+                self.error(
+                    root.loc,
+                    "when starting from state `%s', actors of protocol `%s' cannot be deleted", root.state.name, p.name)
+
+        for ts in p.states.itervalues():
+            if ts.state is not State.DEAD and ts.state not in allvisited:
+                self.error(ts.loc,
+                           "unreachable state `%s' in protocol `%s'",
                            ts.state.name, p.name)
--- a/ipc/ipdl/test/cxx/PTestArrays.ipdl
+++ b/ipc/ipdl/test/cxx/PTestArrays.ipdl
@@ -32,17 +32,17 @@ sync protocol PTestArrays {
     manages PTestArraysSub;
 
 child:
     PTestArraysSub(int i);
 
     Start();
 
 parent:
-    ~PTestArraysSub();
+    __delete__();
 
     sync Test1(int[] i1)
         returns (int[] o1);
 
     sync Test2(PTestArraysSub[] i1)
         returns (PTestArraysSub[] o1);
 
     sync Test3(IntDouble i1,
@@ -79,13 +79,31 @@ parent:
                Unions i4)
         returns (Unions o1,
                  Unions o2,
                  Unions o3,
                  Unions o4);
 
     sync Test10(Unions[] i1)
         returns (Unions[] o1);
+
+
+state START:
+    send Start goto TEST1;
+
+state TEST1:   recv Test1 goto TEST2;
+state TEST2:   recv Test2 goto TEST3;
+state TEST3:   recv Test3 goto TEST4;
+state TEST4:   recv Test4 goto TEST5;
+state TEST5:   recv Test5 goto TEST6;
+state TEST6:   recv Test6 goto TEST7;
+state TEST7:   recv Test7 goto TEST8;
+state TEST8:   recv Test8 goto TEST9;
+state TEST9:   recv Test9 goto TEST10;
+state TEST10:  recv Test10 goto DEAD;
+
+state DEAD:
+    recv __delete__;
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
 
--- a/ipc/ipdl/test/cxx/PTestArraysSub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestArraysSub.ipdl
@@ -1,12 +1,14 @@
 include protocol "PTestArrays.ipdl";
 
 namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestArraysSub {
     manager PTestArrays;
-    // empty
+
+parent:
+    __delete__();
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestDesc.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl
@@ -3,17 +3,16 @@ include protocol "PTestDescSubsub.ipdl";
 
 namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestDesc {
     manages PTestDescSub; 
 child:
     PTestDescSub();
-    ~PTestDescSub();
 
     Test(PTestDescSubsub a);
 
 parent:
     Ok(PTestDescSubsub a);
 
 
 state START:
--- a/ipc/ipdl/test/cxx/PTestDescSub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDescSub.ipdl
@@ -4,14 +4,15 @@ include protocol "PTestDescSubsub.ipdl";
 namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestDescSub {
     manager PTestDesc;
     manages PTestDescSubsub;
 
 child:
+    __delete__();
+
     PTestDescSubsub();
-    ~PTestDescSubsub();
 };
 
 }
 }
--- a/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
@@ -1,13 +1,15 @@
+
 include protocol "PTestDescSub.ipdl";
 
 namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestDescSubsub {
     manager PTestDescSub;
 
-    // empty
+child:
+    __delete__();
 };
 
 }
 }
--- a/ipc/ipdl/test/cxx/PTestLatency.ipdl
+++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl
@@ -1,16 +1,17 @@
 
 namespace mozilla {
 namespace _ipdltest {
 
 
 protocol PTestLatency {
 
 child:
+    __delete__();
     Ping();
     Ping5();
 
 parent:
     Pong();
     Pong5();
 
     // Trial 1: single ping/pong latency
@@ -19,17 +20,18 @@ state PING:
     send Ping5 goto PING4;
 
 state PONG:
     recv Pong goto PING;
 
     // Trial 2: "overlapped" ping/pong latency
 state PING5:
     send Ping5 goto PING4;
-    //send delete; SOMEDAY!
+    send __delete__;
+
 state PING4: send Ping5 goto PING3;
 state PING3: send Ping5 goto PING2;
 state PING2: send Ping5 goto PING1;
 state PING1: send Ping5 goto PONG1;
 
 state PONG1: recv Pong5 goto PONG2;
 state PONG2: recv Pong5 goto PONG3;
 state PONG3: recv Pong5 goto PONG4;
--- a/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
+++ b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
@@ -4,18 +4,16 @@ namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestManyChildAllocs {
     manages PTestManyChildAllocsSub;
 
 child:
     Go();                       // start allocating
 
-    ~PTestManyChildAllocsSub();
-
 parent:
     Done();
 
     PTestManyChildAllocsSub();
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
@@ -1,16 +1,19 @@
 include protocol "PTestManyChildAllocs.ipdl";
 
 namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestManyChildAllocsSub {
     manager PTestManyChildAllocs;
 
+child:
+    __delete__();
+
 parent:
     Hello();
 
     // empty
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestSanity.ipdl
+++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl
@@ -2,30 +2,27 @@
 namespace mozilla {
 namespace _ipdltest {
 
 
 protocol PTestSanity {
 
 child:
     Ping(int zero, float zeroPtFive);
+    __delete__();
 
 parent:
     Pong(int one, float zeroPtTwoFive);
 
-both:
-    UNREACHED();
-
 
 state PING:
     send Ping goto PONG;
 
 state PONG:
     recv Pong goto DEAD;
 
-    // hmm ... maybe support this idiom natively?
 state DEAD:
-    send UNREACHED goto DEAD;
+    send __delete__;
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/TestArrays.cpp
+++ b/ipc/ipdl/test/cxx/TestArrays.cpp
@@ -57,21 +57,22 @@ TestArraysParent::Main()
         if (!SendPTestArraysSubConstructor(i))
             fail("can't alloc actor");
 
     if (!SendStart())
         fail("can't send Start()");
 }
 
 bool
-TestArraysParent::RecvPTestArraysSubDestructor(PTestArraysSubParent* actor)
+TestArraysParent::DeallocPTestArraysSub(PTestArraysSubParent* actor)
 {
     test_assert(Cast(actor).mI == Cast(mKids[0]).mI,
                 "dtor sent to wrong actor");
     mKids.RemoveElementAt(0);
+    delete actor;
     if (mKids.Length() > 0)
         return true;
 
     passed("with flying colors");
 
     static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
     nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
     appShell->Exit();
@@ -316,17 +317,17 @@ TestArraysChild::RecvStart()
     Test5();
     Test6();
     Test7();
     Test8();
     Test9();
     Test10();
 
     for (uint32 i = 0; i < nactors; ++i)
-        if (!SendPTestArraysSubDestructor(mKids[i]))
+        if (!PTestArraysSubChild::Send__delete__(mKids[i]))
             fail("can't send dtor");
 
     return true;
 }
 
 void
 TestArraysChild::Test1()
 {
--- a/ipc/ipdl/test/cxx/TestArrays.h
+++ b/ipc/ipdl/test/cxx/TestArrays.h
@@ -41,24 +41,17 @@ public:
 protected:
     virtual PTestArraysSubParent* AllocPTestArraysSub(const int& i)
     {
         PTestArraysSubParent* actor = new TestArraysSub(i);
         mKids.AppendElement(actor);
         return actor;
     }
 
-    virtual bool DeallocPTestArraysSub(PTestArraysSubParent* actor)
-    {
-        delete actor;
-        return true;
-    }
-
-    virtual bool
-    RecvPTestArraysSubDestructor(PTestArraysSubParent* actor);
+    virtual bool DeallocPTestArraysSub(PTestArraysSubParent* actor);
 
     virtual bool RecvTest1(
             const nsTArray<int>& i1,
             nsTArray<int>* o1);
 
     virtual bool RecvTest2(
             const nsTArray<PTestArraysSubParent*>& i1,
             nsTArray<PTestArraysSubParent*>* o1);
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl
@@ -0,0 +1,14 @@
+// XXX kind of a gray area whether |delete| should be a part of the
+// top-level protocol.  but if it's ever not, this test will break and
+// we'll notice, right?
+protocol DeleteRace {
+parent:
+    M1();
+
+child:
+    delete();
+
+state START:
+    send delete;
+    recv M1 goto START;
+};
--- a/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl
+++ b/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl
@@ -1,9 +1,10 @@
 protocol actorparam_badState {
 
 child:
     Msg(actorparam_badState:FARGEL p);
+    delete();
 
 state S1:
     send Msg goto S1;
-
+    send delete;
 };
--- a/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
+++ b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
@@ -1,8 +1,8 @@
 protocol dtorReserved {
 
-    // it's an error to use dtor syntax for non-dtor messages
+    // it's an error to use old-style dtor syntax
 
 child:
     ~SomeMsg();
 
 };
--- a/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
@@ -1,6 +1,7 @@
 include protocol "managerNoCtor.ipdl";
 
 protocol managedNoCtor {
     manager managerNoCtor;
     // empty
+child: delete();
 };
--- a/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
@@ -1,9 +1,8 @@
 include protocol "managedNoCtor.ipdl";
 
 protocol managerNoCtor {
     manages managedNoCtor;
 
 parent:
     // error: no ctor defined
-    ~managedNoCtor();
 };
--- a/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl
+++ b/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl
@@ -1,29 +1,35 @@
 protocol race_OverlappingMultiOut {
 child:
     Msg1();
     Msg1_();
+    delete(); suppressUndeleteableError()
+
 parent:
     Msg2();
     Msg2_();
 
+
+start state _:
+    send delete;
+    send suppressUndeleteableError goto S10;
+
     // *** ERROR: send/recv of Msg1/Msg2 in state S10 goes to overlapping
     // sets { S11, S12 }, { S12, S13 } and thus can't be unidirectional
-start state S10:
+state S10:
     send Msg1 goto S11 or S12;
     recv Msg2 goto S12 or S13;
 
 state S11:
     recv Msg2 goto S14;
 
 state S12:
     send Msg1 goto S14;
     recv Msg2 goto S14;
 
 state S13:
     send Msg1 goto S14;
 
 state S14:
     send Msg1 goto S14;
     recv Msg2 goto S14;
-
 };
--- a/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl
+++ b/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl
@@ -1,25 +1,29 @@
 protocol race_ViolateSameDirection {
 child:
     Msg1();
     Msg1_();
+    delete(); suppressUndeleteableError();
 parent:
     Msg2();
     Msg2_();
 
     // *** ERROR: state S7 doesn't have all-same-direction
-start state S6:
+start state _:
+    send delete;
+    send suppressUndeleteableError goto S6;
+
+state S6:
     send Msg1 goto S7;
     recv Msg2 goto S8;
 
 state S7:
     recv Msg2 goto S9;
     send Msg1 goto S9;
 
 state S8:
     send Msg1 goto S9;
 
 state S9:
     send Msg1 goto S9;
     recv Msg2 goto S9;
-
 };
--- a/ipc/ipdl/test/ipdl/error/redefState.ipdl
+++ b/ipc/ipdl/test/ipdl/error/redefState.ipdl
@@ -1,11 +1,14 @@
 protocol redefState {
 
     // error: redefining state in state machine
 child:
     Msg();
+    delete();
 
 state S1: send Msg goto S1;
 
 state S1: send Msg goto S1;
 
+start state _:
+    send delete;
 };
--- a/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl
+++ b/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl
@@ -1,12 +1,12 @@
 protocol repeatedOutState {
-child: Msg();
+child: Msg(); delete();
 
     // error: S2 repeated in multi-out set
 
 state S1:
     send Msg goto S2 or S2 or S4;
 
 state S2: send Msg goto S2;
 state S3: send Msg goto S3;
-state S4: send Msg goto S4;
+state S4: send Mesg goto S4;  send delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl
@@ -1,9 +1,12 @@
 protocol trans_WrongDirection {
 
 child:
     Msg();
+    delete();
 
 state S1:
     recv Msg goto S1;
 
+start state _:
+    send delete();
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl
@@ -1,9 +1,12 @@
 protocol trans_WrongDirection2 {
 
 parent:
     Msg();
+    delete();
 
 state S1:
     send Msg goto S1;
 
+start state _:
+    recv delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl
@@ -1,9 +1,12 @@
 sync protocol trans_WrongDirection3 {
 
 parent:
     sync Msg();
+    delete();
 
 state S1:
     send Msg goto S1;
 
+start state _:
+    recv delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl
@@ -1,9 +1,12 @@
 rpc protocol trans_WrongDirection4 {
 
 child:
     rpc Msg();
+    delete();
 
 state S1:
     answer Msg goto S1;
 
+start state _:
+    send delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl
@@ -1,9 +1,12 @@
 rpc protocol trans_WrongDirection5 {
 
 parent:
     rpc Msg();
+    delete()
 
 state S1:
     call Msg goto S1;
 
+start state_:
+    recv delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl
@@ -1,9 +1,10 @@
 protocol trans_WrongName {
 
 child:
     Msg();
+    delete();
 
 state S1:
     call Msg goto S1;
-
+    send delete();
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl
@@ -1,9 +1,10 @@
 protocol trans_WrongName2 {
 
 parent:
     Msg();
+    delete();
 
 state S1:
     answer Msg goto S1;
-
+    recv delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl
@@ -1,9 +1,10 @@
 sync protocol trans_WrongName3 {
 
 parent:
     sync Msg();
+    delete();
 
 state S1:
     answer Msg goto S1;
-
+    recv delete();
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl
@@ -1,9 +1,10 @@
 rpc protocol trans_WrongName4 {
 
 child:
     rpc Msg();
+    delete();
 
 state S1:
     send Msg goto S1;
-
+    send delete;
 };
--- a/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl
+++ b/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl
@@ -1,9 +1,10 @@
 rpc protocol trans_WrongName5 {
 
 parent:
     rpc Msg();
+    delete();
 
 state S1:
     recv Msg goto S1;
-
+    recv delete;
 };
--- a/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
@@ -1,7 +1,6 @@
 protocol undeclProtocol {
     manages undeclared;
 
 child:
     undeclared();
-    ~undeclared();
 };
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl
@@ -0,0 +1,5 @@
+protocol unreachedDelete {
+child:  M1(); delete();
+
+state S1:  send M1 goto S1;
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl
@@ -0,0 +1,10 @@
+protocol unreachedDeleteMultiStart {
+child:
+    M1(); M2(); delete();
+
+start state S1:  send M1 goto S2;
+state S2:        send delete;
+
+start state S3:  send M2 goto S4;
+state S4:        send M1 goto S3;
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/Delete.ipdl
@@ -0,0 +1,8 @@
+include protocol "DeleteSub.ipdl";
+
+sync protocol Delete {
+    manages DeleteSub;
+
+child:
+    DeleteSub();
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl
@@ -0,0 +1,12 @@
+include protocol "Delete.ipdl";
+
+sync protocol DeleteSub {
+    manager Delete;
+
+parent:
+    sync __delete__(int x) returns (double d);
+
+state START:
+    recv __delete__;
+};
+
--- a/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl
@@ -1,9 +1,10 @@
 protocol actorparam_state {
 
 child:
     Msg(actorparam_state:S1 p);
+    __delete__();
 
 state S1:
     send Msg goto S1;
-
+    send __delete__;
 };
--- a/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl
@@ -2,10 +2,9 @@ include protocol "array_OfActorsSub.ipdl
 
 protocol array_OfActors {
     manages array_OfActorsSub;
 
 child:
     Msg(array_OfActorsSub[] p);
 
     array_OfActorsSub();
-    ~array_OfActorsSub();
 };
--- a/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl
@@ -1,7 +1,7 @@
 include protocol "array_OfActors.ipdl";
 
 protocol array_OfActorsSub {
     manager array_OfActors;
 
-    // empty
+child: __delete__();
 };
--- a/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl
@@ -1,6 +1,8 @@
 include protocol "managerProtocol.ipdl";
 
 protocol managedProtocol {
     manager managerProtocol;
-    // empty
+
+child:
+    __delete__();
 };
--- a/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
@@ -2,11 +2,10 @@ include protocol "managedProtocol.ipdl";
 
 // sanity check of managed/manager protocols
 
 protocol managerProtocol {
     manages managedProtocol;
 
 parent:
     managedProtocol(int i);
-    ~managedProtocol(int i);
 
 };
--- a/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
@@ -1,11 +1,11 @@
 protocol multiStartState {
 
-child: Msg();
+child: Msg(); __delete__();
 
 start state S1:
-    send Msg goto S1;
+    send Msg goto S1; send __delete__;
 
 start state S2:
-    send Msg goto S2;
+    send Msg goto S2; send __delete__;
 
 };
--- a/ipc/testshell/PTestShell.ipdl
+++ b/ipc/testshell/PTestShell.ipdl
@@ -43,18 +43,17 @@ namespace ipc {
 
 protocol PTestShell
 {
   manager PContentProcess;
 
   manages PTestShellCommand;
 
 child:
+  __delete__();
+
   ExecuteCommand(nsString aCommand);
 
   PTestShellCommand(nsString aCommand);
-
-parent:
-  ~PTestShellCommand(nsString aResponse);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/testshell/PTestShellCommand.ipdl
+++ b/ipc/testshell/PTestShellCommand.ipdl
@@ -38,12 +38,15 @@
 include protocol "PTestShell.ipdl";
 
 namespace mozilla {
 namespace ipc {
 
 protocol PTestShellCommand
 {
   manager PTestShell;
+
+parent:
+  __delete__(nsString aResponse);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/testshell/TestShellChild.cpp
+++ b/ipc/testshell/TestShellChild.cpp
@@ -58,18 +58,17 @@ TestShellChild::RecvExecuteCommand(const
 
 PTestShellCommandChild*
 TestShellChild::AllocPTestShellCommand(const nsString& aCommand)
 {
   return new PTestShellCommandChild();
 }
 
 bool
-TestShellChild::DeallocPTestShellCommand(PTestShellCommandChild* aCommand,
-                                         const nsString& aResponse)
+TestShellChild::DeallocPTestShellCommand(PTestShellCommandChild* aCommand)
 {
   delete aCommand;
   return true;
 }
 
 bool
 TestShellChild::RecvPTestShellCommandConstructor(PTestShellCommandChild* aActor,
                                                  const nsString& aCommand)
@@ -79,10 +78,10 @@ TestShellChild::RecvPTestShellCommandCon
     return false;
   }
 
   nsString response;
   if (!mXPCShell->EvaluateString(aCommand, &response)) {
     return false;
   }
 
-  return SendPTestShellCommandDestructor(aActor, response);
+  return PTestShellCommandChild::Send__delete__(aActor, response);
 }
--- a/ipc/testshell/TestShellChild.h
+++ b/ipc/testshell/TestShellChild.h
@@ -59,18 +59,17 @@ public:
   PTestShellCommandChild*
   AllocPTestShellCommand(const nsString& aCommand);
 
   bool
   RecvPTestShellCommandConstructor(PTestShellCommandChild* aActor,
                                    const nsString& aCommand);
 
   bool
-  DeallocPTestShellCommand(PTestShellCommandChild* aCommand,
-                           const nsString& aResponse);
+  DeallocPTestShellCommand(PTestShellCommandChild* aCommand);
 
   void SetXPCShell(XPCShellEnvironment* aXPCShell) {
     mXPCShell = aXPCShell;
   }
 
 private:
   nsAutoPtr<XPCShellEnvironment> mXPCShell;
 };
--- a/ipc/testshell/TestShellParent.cpp
+++ b/ipc/testshell/TestShellParent.cpp
@@ -44,31 +44,28 @@ using mozilla::ipc::PTestShellCommandPar
 
 PTestShellCommandParent*
 TestShellParent::AllocPTestShellCommand(const nsString& aCommand)
 {
   return new TestShellCommandParent();
 }
 
 bool
-TestShellParent::DeallocPTestShellCommand(PTestShellCommandParent* aActor,
-                                          const nsString& aResponse)
+TestShellParent::DeallocPTestShellCommand(PTestShellCommandParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 bool
-TestShellParent::RecvPTestShellCommandDestructor(PTestShellCommandParent* aActor,
-                                                 const nsString& aResponse)
+TestShellParent::CommandDone(TestShellCommandParent* command,
+                             const nsString& aResponse)
 {
-  TestShellCommandParent* command =
-    reinterpret_cast<TestShellCommandParent*>(aActor);
-
-  JSBool ok = command->RunCallback(aResponse);
+  // XXX what should happen if the callback fails?
+  /*JSBool ok = */command->RunCallback(aResponse);
   command->ReleaseCallback();
 
   return true;
 }
 
 JSBool
 TestShellCommandParent::SetCallback(JSContext* aCx,
                                     jsval aCallback)
--- a/ipc/testshell/TestShellParent.h
+++ b/ipc/testshell/TestShellParent.h
@@ -1,8 +1,11 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/
  *
@@ -42,44 +45,53 @@
 
 #include "jsapi.h"
 #include "nsAutoJSValHolder.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace ipc {
 
+class TestShellCommandParent;
+
+
+class TestShellParent : public PTestShellParent
+{
+public:
+  PTestShellCommandParent*
+  AllocPTestShellCommand(const nsString& aCommand);
+
+  bool
+  DeallocPTestShellCommand(PTestShellCommandParent* aActor);
+
+  bool
+  CommandDone(TestShellCommandParent* aActor, const nsString& aResponse);
+};
+
+
 class TestShellCommandParent : public PTestShellCommandParent
 {
 public:
   TestShellCommandParent() : mCx(NULL) { }
 
   JSBool SetCallback(JSContext* aCx,
                      jsval aCallback);
 
   JSBool RunCallback(const nsString& aResponse);
 
   void ReleaseCallback();
 
+protected:
+  bool Recv__delete__(const nsString& aResponse) {
+    return static_cast<TestShellParent*>(Manager())->CommandDone(
+      this, aResponse);
+  }
+
 private:
   JSContext* mCx;
   nsAutoJSValHolder mCallback;
 };
 
-class TestShellParent : public PTestShellParent
-{
-public:
-  PTestShellCommandParent*
-  AllocPTestShellCommand(const nsString& aCommand);
-
-  bool
-  DeallocPTestShellCommand(PTestShellCommandParent* aActor,
-                           const nsString& aResponse);
-
-  bool
-  RecvPTestShellCommandDestructor(PTestShellCommandParent* aActor,
-                                  const nsString& aResponse);
-};
 
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* ipc_testshell_TestShellParent_h */
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -47,15 +47,16 @@ namespace net {
 
 //-------------------------------------------------------------------
 protocol PNecko
 {
   manager PContentProcess;
   manages PHttpChannel;
 
 parent:
+  __delete__();
+
   PHttpChannel();
-  ~PHttpChannel();
 };
 
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/src/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/src/PHttpChannel.ipdl
@@ -45,15 +45,17 @@ namespace net {
 
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
 {
   manager PNecko;
 
 parent:
+  __delete__();
+
   asyncOpen(nsCString uri);
 };
 
 
 } // namespace net
 } // namespace mozilla