Bug 516522 - CPOW: Cross-Process (JavaScript) Object Wrapper. r=mrbkap r=bent sr=jst
authorBen Newman <bnewman@mozilla.com>
Fri, 06 Nov 2009 12:43:39 -0800
changeset 46761 e483a5868df5e65597a28f846e81286a233a6801
parent 46760 faa984df7f453d2adb7adce1bc184a407c43311f
child 46762 55b6530567ddb7fea49254b8635ac024214a0015
push idunknown
push userunknown
push dateunknown
reviewersmrbkap, bent, jst
bugs516522
milestone1.9.3a4pre
Bug 516522 - CPOW: Cross-Process (JavaScript) Object Wrapper. r=mrbkap r=bent sr=jst
content/base/public/nsIFrameLoader.idl
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/base/src/nsObjectLoadingContent.cpp
content/html/content/src/nsGenericHTMLElement.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
dom/ipc/ContentProcessChild.cpp
dom/ipc/ContentProcessChild.h
dom/ipc/PIFrameEmbedding.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
ipc/ipdl/Makefile.in
ipc/testshell/PTestShell.ipdl
ipc/testshell/TestShellChild.cpp
ipc/testshell/TestShellChild.h
ipc/testshell/TestShellParent.cpp
ipc/testshell/TestShellParent.h
ipc/testshell/XPCShellEnvironment.h
js/src/ipc/CPOWTypes.h
js/src/ipc/ContextWrapperChild.h
js/src/ipc/ContextWrapperParent.h
js/src/ipc/Makefile.in
js/src/ipc/ObjectWrapperChild.cpp
js/src/ipc/ObjectWrapperChild.h
js/src/ipc/ObjectWrapperParent.cpp
js/src/ipc/ObjectWrapperParent.h
js/src/ipc/PContextWrapper.ipdl
js/src/ipc/PObjectWrapper.ipdl
js/src/ipc/ipdl.mk
js/src/xpconnect/shell/xpcshell.cpp
toolkit/library/libxul-config.mk
toolkit/toolkit-makefiles.sh
toolkit/toolkit-tiers.mk
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXULAppAPI.h
--- a/content/base/public/nsIFrameLoader.idl
+++ b/content/base/public/nsIFrameLoader.idl
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIDocShell;
 interface nsIURI;
 interface nsIFrame;
 interface nsIChromeFrameMessageManager;
+interface nsIVariant;
 
 [scriptable, uuid(e511f61f-97db-448a-8b29-a10c470df3fa)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
@@ -116,25 +117,27 @@ interface nsIFrameLoader : nsISupports
                                 in long aCharCode,
                                 in long aModifiers,
                                 [optional] in boolean aPreventDefault);
 
 };
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
-[scriptable, uuid(8f3b12a0-35ae-4e0d-9152-8e0d7e49d446)]
+[scriptable, uuid(5879040e-83e9-40e3-b2bb-5ddf43b76e47)]
 interface nsIFrameLoaderOwner : nsISupports
 {
   /**
    * The frame loader owned by this nsIFrameLoaderOwner
    */
   readonly attribute nsIFrameLoader frameLoader;
   [noscript, notxpcom] alreadyAddRefed_nsFrameLoader GetFrameLoader();
 
+  readonly attribute nsIVariant crossProcessObjectWrapper;
+
   /**
    * Swap frame loaders with the given nsIFrameLoaderOwner.  This may
    * only be posible in a very limited range of circumstances, or
    * never, depending on the object implementing this interface.
    *
    * @throws NS_ERROR_NOT_IMPLEMENTED if the swapping logic is not
    *   implemented for the two given frame loader owners.
    * @throws NS_ERROR_DOM_SECURITY_ERR if the swap is not allowed on
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -70,16 +70,18 @@
 #include "nsIWebNavigation.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIBaseWindow.h"
 #include "nsContentUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIJSContextStack.h"
 #include "nsUnicharUtils.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScrollable.h"
 #include "nsFrameLoader.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIFrame.h"
 #include "nsIFrameFrame.h"
@@ -118,16 +120,18 @@
 #ifdef MOZ_IPC
 #include "ContentProcessParent.h"
 #include "TabParent.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 #endif
 
+#include "jsapi.h"
+
 class nsAsyncDocShellDestroyer : public nsRunnable
 {
 public:
   nsAsyncDocShellDestroyer(nsIDocShell* aDocShell)
     : mDocShell(aDocShell)
   {
   }
 
@@ -1589,16 +1593,34 @@ nsFrameLoader::SendCrossProcessKeyEvent(
     mChildProcess->SendKeyEvent(aType, aKeyCode, aCharCode, aModifiers,
                                 aPreventDefault);
     return NS_OK;
   }
 #endif
   return NS_ERROR_FAILURE;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::GetCrossProcessObjectWrapper(nsIVariant** cpow)
+{
+   nsIXPConnect* xpc;
+   nsIThreadJSContextStack* stack;
+   JSContext* cx;
+   JSObject* global;
+ 
+   if ((xpc = nsContentUtils::XPConnect()) &&
+       (stack = nsContentUtils::ThreadJSContextStack()) &&
+       NS_SUCCEEDED(stack->Peek(&cx)) && cx &&
+       mChildProcess->GetGlobalJSObject(cx, &global)) {
+     return xpc->JSToVariant(cx, OBJECT_TO_JSVAL(global), cpow);
+   }
+  
+   return NS_ERROR_NOT_AVAILABLE;
+}
+
 nsresult
 nsFrameLoader::CreateStaticClone(nsIFrameLoader* aDest)
 {
   nsFrameLoader* dest = static_cast<nsFrameLoader*>(aDest);
   dest->MaybeCreateDocShell();
   NS_ENSURE_STATE(dest->mDocShell);
 
   nsCOMPtr<nsIDOMDocument> dummy = do_GetInterface(dest->mDocShell);
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -135,16 +135,17 @@ public:
   // frame loader owner needs to call this, and pass in the two references to
   // nsRefPtrs for frame loaders that need to be swapped.
   nsresult SwapWithOtherLoader(nsFrameLoader* aOther,
                                nsRefPtr<nsFrameLoader>& aFirstToSwap,
                                nsRefPtr<nsFrameLoader>& aSecondToSwap);
 
 #ifdef MOZ_IPC
   mozilla::dom::PIFrameEmbeddingParent* GetChildProcess();
+  NS_IMETHOD GetCrossProcessObjectWrapper(nsIVariant** cpow);
 #endif
 
   nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; }
 
 private:
 
 #ifdef MOZ_IPC
   bool ShouldUseRemoteProcess();
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -790,16 +790,22 @@ nsObjectLoadingContent::GetFrameLoader()
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP
+nsObjectLoadingContent::GetCrossProcessObjectWrapper(nsIVariant**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 // nsIObjectLoadingContent
 NS_IMETHODIMP
 nsObjectLoadingContent::GetActualType(nsACString& aType)
 {
   aType = mContentType;
   return NS_OK;
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2770,16 +2770,23 @@ nsGenericHTMLFrameElement::GetFrameLoade
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
 {
   // We don't support this yet
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP
+nsGenericHTMLFrameElement::GetCrossProcessObjectWrapper(nsIVariant**)
+{
+  // We don't support this yet
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 nsresult
 nsGenericHTMLFrameElement::LoadSrc()
 {
   nsresult rv = EnsureFrameLoader();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!mFrameLoader) {
     return NS_OK;
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1998,16 +1998,22 @@ nsXULElement::SwapFrameLoaders(nsIFrameL
     }
 
     return
         ourSlots->mFrameLoader->SwapWithOtherLoader(otherSlots->mFrameLoader,
                                                     ourSlots->mFrameLoader,
                                                     otherSlots->mFrameLoader);
 }
 
+NS_IMETHODIMP
+nsXULElement::GetCrossProcessObjectWrapper(nsIVariant** cpow)
+{
+    nsRefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
+    return frameLoader->GetCrossProcessObjectWrapper(cpow);
+}
 
 NS_IMETHODIMP
 nsXULElement::GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement)
 {
     for (nsIContent* current = GetParent(); current;
          current = current->GetParent()) {
         if (current->NodeInfo()->Equals(nsGkAtoms::listbox,
                                         kNameSpaceID_XUL)) {
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -558,16 +558,18 @@ public:
     virtual PRInt32 IntrinsicState() const;
 
     nsresult GetStyle(nsIDOMCSSStyleDeclaration** aStyle);
 
     nsresult GetFrameLoader(nsIFrameLoader** aFrameLoader);
     already_AddRefed<nsFrameLoader> GetFrameLoader();
     nsresult SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner);
 
+    NS_IMETHOD GetCrossProcessObjectWrapper(nsIVariant** cpow);
+
     virtual void RecompileScriptEventListeners();
 
     // This function should ONLY be used by BindToTree implementations.
     // The function exists solely because XUL elements store the binding
     // parent as a member instead of in the slots, as nsGenericElement does.
     void SetXULBindingParent(nsIContent* aBindingParent)
     {
       mBindingParent = aBindingParent;
--- a/dom/ipc/ContentProcessChild.cpp
+++ b/dom/ipc/ContentProcessChild.cpp
@@ -40,16 +40,18 @@
 #include <QApplication>
 #endif
 
 #include "ContentProcessChild.h"
 #include "TabChild.h"
 
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/net/NeckoChild.h"
+#include "mozilla/ipc/XPCShellEnvironment.h"
+#include "mozilla/jsipc/PContextWrapperChild.h"
 
 #include "nsXULAppAPI.h"
 
 #include "base/message_loop.h"
 #include "base/task.h"
 
 #include "nsChromeRegistryContent.h"
 #include "mozilla/chrome/RegistryMessageUtils.h"
@@ -126,16 +128,23 @@ ContentProcessChild::AllocPTestShell()
 
 bool
 ContentProcessChild::DeallocPTestShell(PTestShellChild* shell)
 {
     mTestShells.RemoveElement(shell);
     return true;
 }
 
+bool
+ContentProcessChild::RecvPTestShellConstructor(PTestShellChild* actor)
+{
+    actor->SendPContextWrapperConstructor()->SendPObjectWrapperConstructor(true);
+    return true;
+}
+
 PNeckoChild* 
 ContentProcessChild::AllocPNecko()
 {
     return new NeckoChild();
 }
 
 bool 
 ContentProcessChild::DeallocPNecko(PNeckoChild* necko)
--- a/dom/ipc/ContentProcessChild.h
+++ b/dom/ipc/ContentProcessChild.h
@@ -73,16 +73,17 @@ public:
     /* if you remove this, please talk to cjones or dougt */
     virtual bool RecvDummy(Shmem& foo) { return true; }
 
     virtual PIFrameEmbeddingChild* AllocPIFrameEmbedding();
     virtual bool DeallocPIFrameEmbedding(PIFrameEmbeddingChild*);
 
     virtual PTestShellChild* AllocPTestShell();
     virtual bool DeallocPTestShell(PTestShellChild*);
+    virtual bool RecvPTestShellConstructor(PTestShellChild*);
 
     virtual PNeckoChild* AllocPNecko();
     virtual bool DeallocPNecko(PNeckoChild*);
 
     virtual bool RecvRegisterChrome(const nsTArray<ChromePackage>& packages,
                                     const nsTArray<ResourceMapping>& resources,
                                     const nsTArray<OverrideMapping>& overrides);
 
--- a/dom/ipc/PIFrameEmbedding.ipdl
+++ b/dom/ipc/PIFrameEmbedding.ipdl
@@ -35,16 +35,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 include protocol "PContentProcess.ipdl";
 include protocol "PDocumentRenderer.ipdl";
 include protocol "PDocumentRendererShmem.ipdl";
+include protocol "PContextWrapper.ipdl";
 
 include "mozilla/TabTypes.h";
 include "TabMessageUtils.h";
 include "gfxMatrix.h";
 
 using MagicWindowHandle;
 using RemoteDOMEvent;
 using gfxMatrix;
@@ -52,31 +53,34 @@ using gfxMatrix;
 namespace mozilla {
 namespace dom {
 
 rpc protocol PIFrameEmbedding
 {
     manager PContentProcess;
     manages PDocumentRenderer;
     manages PDocumentRendererShmem;
+    manages PContextWrapper;
 
 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);
 
     rpc createWindow() returns (PIFrameEmbedding window);
 
+    PContextWrapper();
+
     sync sendSyncMessageToParent(nsString aMessage, nsString aJSON) returns (nsString[] retval);
     sendAsyncMessageToParent(nsString aMessage, nsString aJSON);
 child:
     createWidget(MagicWindowHandle parentWidget);
 
     loadURL(nsCString uri);
 
     move(PRUint32 x,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -32,16 +32,18 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "TabChild.h"
+#include "mozilla/dom/PContentProcessChild.h"
+#include "mozilla/jsipc/ContextWrapperChild.h"
 
 #include "nsIWebBrowser.h"
 #include "nsEmbedCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIBaseWindow.h"
 #include "nsIDOMWindow.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsThreadUtils.h"
@@ -63,29 +65,32 @@
 #include "nsIXPCSecurityManager.h"
 #include "nsIJSContextStack.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsScriptLoader.h"
 #include "nsPIWindowRoot.h"
 #include "nsIScriptContext.h"
 #include "nsPresContext.h"
+#include "nsIDocument.h"
+#include "nsIScriptGlobalObject.h"
 
 #ifdef MOZ_WIDGET_QT
 #include <QX11EmbedWidget>
 #include <QGraphicsView>
 #include <QGraphicsWidget>
 #endif
 
 #ifdef MOZ_WIDGET_GTK2
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 #endif
 
 using namespace mozilla::dom;
+using namespace mozilla::jsipc;
 
 NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener)
 
 NS_IMETHODIMP
 ContentListener::HandleEvent(nsIDOMEvent* aEvent)
 {
   RemoteDOMEvent remoteEvent;
   remoteEvent.mEvent = do_QueryInterface(aEvent);
@@ -370,16 +375,19 @@ TabChild::RecvloadURL(const nsCString& u
     printf("loading %s, %d\n", uri.get(), NS_IsMainThread());
 
     nsresult rv = mWebNav->LoadURI(NS_ConvertUTF8toUTF16(uri).get(),
                                    nsIWebNavigation::LOAD_FLAGS_NONE,
                                    NULL, NULL, NULL);
     if (NS_FAILED(rv)) {
         NS_WARNING("mWebNav->LoadURI failed. Eating exception, what else can I do?");
     }
+
+    SendPContextWrapperConstructor()->SendPObjectWrapperConstructor(true);
+
     return true;
 }
 
 bool
 TabChild::Recvmove(const PRUint32& x,
                      const PRUint32& y,
                      const PRUint32& width,
                      const PRUint32& height)
@@ -428,16 +436,47 @@ TabChild::RecvsendKeyEvent(const nsStrin
   nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
   NS_ENSURE_TRUE(utils, true);
   PRBool ignored = PR_FALSE;
   utils->SendKeyEvent(aType, aKeyCode, aCharCode,
                       aModifiers, aPreventDefault, &ignored);
   return true;
 }
 
+static JSContext*
+GetJSContextFrom(nsIWebNavigation* webNav)
+{
+    nsCOMPtr<nsIDOMDocument> domDocument;
+    nsCOMPtr<nsIDocument> document;
+    nsCOMPtr<nsIScriptGlobalObject> global;
+    nsCOMPtr<nsIScriptContext> context;
+
+    if (NS_SUCCEEDED(webNav->GetDocument(getter_AddRefs(domDocument))) &&
+        (document = do_QueryInterface(domDocument)) &&
+        (global = do_QueryInterface(document->GetScriptGlobalObject())) &&
+        (context = do_QueryInterface(global->GetContext()))) {
+        return static_cast<JSContext*>(context->GetNativeContext());
+    }
+
+    return NULL;
+}
+
+PContextWrapperChild*
+TabChild::AllocPContextWrapper()
+{
+    return new ContextWrapperChild(GetJSContextFrom(mWebNav));
+}
+
+bool
+TabChild::DeallocPContextWrapper(PContextWrapperChild* actor)
+{
+    delete actor;
+    return true;
+}
+
 mozilla::ipc::PDocumentRendererChild*
 TabChild::AllocPDocumentRenderer(
         const PRInt32& x,
         const PRInt32& y,
         const PRInt32& w,
         const PRInt32& h,
         const nsString& bgcolor,
         const PRUint32& flags,
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -64,16 +64,21 @@
 #include "nsDOMEventTargetHelper.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 
 class gfxMatrix;
 
 namespace mozilla {
+
+namespace jsipc {
+class PContextWrapperChild;
+}
+
 namespace dom {
 
 class TabChild;
 
 class TabChildGlobal : public nsDOMEventTargetHelper,
                        public nsIContentFrameMessageManager,
                        public nsIScriptObjectPrincipal,
                        public nsIScriptContextPrincipal
@@ -223,16 +228,20 @@ public:
             const PRInt32& aBufH,
             Shmem& aBuf);
 
     nsIWebNavigation* WebNavigation() { return mWebNav; }
 
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
+
+    virtual PContextWrapperChild* AllocPContextWrapper();
+    virtual bool DeallocPContextWrapper(PContextWrapperChild* actor);
+
 private:
     bool InitTabChildGlobal();
 
     nsCOMPtr<nsIWebNavigation> mWebNav;
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> mRootGlobal;
 
     JSContext* mCx;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -35,30 +35,33 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "TabParent.h"
 
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/DocumentRendererShmemParent.h"
+#include "mozilla/jsipc/ContextWrapperParent.h"
 
 #include "nsIURI.h"
 #include "nsFocusManager.h"
 #include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIDOMElement.h"
 #include "nsEventDispatcher.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsFrameLoader.h"
 
 using mozilla::ipc::DocumentRendererParent;
 using mozilla::ipc::DocumentRendererShmemParent;
+using mozilla::jsipc::PContextWrapperParent;
+using mozilla::jsipc::ContextWrapperParent;
 
 namespace mozilla {
 namespace dom {
 
 TabParent::TabParent()
 {
 }
 
@@ -168,16 +171,42 @@ TabParent::AllocPDocumentRendererShmem(c
 
 bool
 TabParent::DeallocPDocumentRendererShmem(PDocumentRendererShmemParent* actor)
 {
     delete actor;
     return true;
 }
 
+PContextWrapperParent*
+TabParent::AllocPContextWrapper()
+{
+    return new ContextWrapperParent();
+}
+
+bool
+TabParent::DeallocPContextWrapper(PContextWrapperParent* actor)
+{
+    delete actor;
+    return true;
+}
+
+bool
+TabParent::GetGlobalJSObject(JSContext* cx, JSObject** globalp)
+{
+    // TODO Unify this code with TestShellParent::GetGlobalJSObject.
+    nsTArray<PContextWrapperParent*> cwps(1);
+    ManagedPContextWrapperParent(cwps);
+    if (cwps.Length() < 1)
+        return false;
+    NS_ASSERTION(cwps.Length() == 1, "More than one PContextWrapper?");
+    ContextWrapperParent* cwp = static_cast<ContextWrapperParent*>(cwps[0]);
+    return (cwp->GetGlobalJSObject(cx, globalp));
+}
+
 void
 TabParent::SendMouseEvent(const nsAString& aType, float aX, float aY,
                           PRInt32 aButton, PRInt32 aClickCount,
                           PRInt32 aModifiers, PRBool aIgnoreRootScrollFrame)
 {
   SendsendMouseEvent(nsString(aType), aX, aY, aButton, aClickCount,
                      aModifiers, aIgnoreRootScrollFrame);
 }
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -45,17 +45,25 @@
 
 #include "nsCOMPtr.h"
 #include "nsIBrowserDOMWindow.h"
 
 class nsIURI;
 class nsIDOMElement;
 class gfxMatrix;
 
+struct JSContext;
+struct JSObject;
+
 namespace mozilla {
+
+namespace jsipc {
+class PContextWrapperParent;
+}
+
 namespace dom {
 
 class TabParent : public PIFrameEmbeddingParent
 {
 public:
     TabParent();
     virtual ~TabParent();
     void SetOwnerElement(nsIDOMElement* aElement) { mFrameElement = aElement; }
@@ -101,16 +109,21 @@ public:
             const PRUint32& flags,
             const bool& flush,
             const gfxMatrix& aMatrix,
             const PRInt32& bufw,
             const PRInt32& bufh,
             Shmem& buf);
     virtual bool DeallocPDocumentRendererShmem(PDocumentRendererShmemParent* actor);
 
+    virtual PContextWrapperParent* AllocPContextWrapper();
+    virtual bool DeallocPContextWrapper(PContextWrapperParent* actor);
+
+    bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
+
 protected:
     nsIDOMElement* mFrameElement;
     nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -55,16 +55,17 @@ EXPORT_LIBRARY = 1
 ##
 IPDLDIRS =  \
   dom/plugins  \
   dom/ipc  \
   netwerk/ipc  \
   netwerk/protocol/http/src  \
   ipc/ipdl/test/cxx  \
   ipc/testshell  \
+  js/src/ipc  \
   $(NULL)
 ##-----------------------------------------------------------------------------
 
 ifdef MOZ_IPDL_TESTS
 DIRS += test
 endif
 
 vpath %.ipdl $(topsrcdir)
--- a/ipc/testshell/PTestShell.ipdl
+++ b/ipc/testshell/PTestShell.ipdl
@@ -32,28 +32,34 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 include protocol "PContentProcess.ipdl";
 include protocol "PTestShellCommand.ipdl";
+include protocol "PContextWrapper.ipdl";
 
 namespace mozilla {
 namespace ipc {
 
-protocol PTestShell
+rpc protocol PTestShell
 {
   manager PContentProcess;
 
   manages PTestShellCommand;
+  manages PContextWrapper;
 
 child:
   __delete__();
 
   ExecuteCommand(nsString aCommand);
 
   PTestShellCommand(nsString aCommand);
+
+parent:
+  PContextWrapper();
+
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/testshell/TestShellChild.cpp
+++ b/ipc/testshell/TestShellChild.cpp
@@ -30,20 +30,23 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "TestShellChild.h"
+#include "mozilla/jsipc/ContextWrapperChild.h"
 
 using mozilla::ipc::TestShellChild;
 using mozilla::ipc::PTestShellCommandChild;
 using mozilla::ipc::XPCShellEnvironment;
+using mozilla::jsipc::PContextWrapperChild;
+using mozilla::jsipc::ContextWrapperChild;
 
 TestShellChild::TestShellChild()
 : mXPCShell(XPCShellEnvironment::CreateEnvironment())
 {
 }
 
 bool
 TestShellChild::RecvExecuteCommand(const nsString& aCommand)
@@ -80,8 +83,25 @@ TestShellChild::RecvPTestShellCommandCon
 
   nsString response;
   if (!mXPCShell->EvaluateString(aCommand, &response)) {
     return false;
   }
 
   return PTestShellCommandChild::Send__delete__(aActor, response);
 }
+
+PContextWrapperChild*
+TestShellChild::AllocPContextWrapper()
+{
+  JSContext* cx;
+  if (mXPCShell && (cx = mXPCShell->GetContext())) {
+    return new ContextWrapperChild(cx);
+  }
+  return NULL;
+}
+
+bool
+TestShellChild::DeallocPContextWrapper(PContextWrapperChild* actor)
+{
+  delete actor;
+  return true;
+}
--- a/ipc/testshell/TestShellChild.h
+++ b/ipc/testshell/TestShellChild.h
@@ -39,16 +39,21 @@
 
 #include "mozilla/ipc/PTestShellChild.h"
 #include "mozilla/ipc/PTestShellCommandChild.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 
 #include "nsAutoPtr.h"
 
 namespace mozilla {
+
+namespace jsipc {
+class PContextWrapperChild;
+}
+
 namespace ipc {
 
 class XPCShellEnvironment;
 
 class TestShellChild : public PTestShellChild
 {
 public:
   TestShellChild();
@@ -61,20 +66,19 @@ public:
 
   bool
   RecvPTestShellCommandConstructor(PTestShellCommandChild* aActor,
                                    const nsString& aCommand);
 
   bool
   DeallocPTestShellCommand(PTestShellCommandChild* aCommand);
 
-  void SetXPCShell(XPCShellEnvironment* aXPCShell) {
-    mXPCShell = aXPCShell;
-  }
-
+  PContextWrapperChild* AllocPContextWrapper();
+  bool DeallocPContextWrapper(PContextWrapperChild* actor);
+  
 private:
   nsAutoPtr<XPCShellEnvironment> mXPCShell;
 };
 
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* ipc_testshell_TestShellChild_h */
--- a/ipc/testshell/TestShellParent.cpp
+++ b/ipc/testshell/TestShellParent.cpp
@@ -30,22 +30,25 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "TestShellParent.h"
+#include "mozilla/jsipc/ContextWrapperParent.h"
 
 #include "nsAutoPtr.h"
 
 using mozilla::ipc::TestShellParent;
 using mozilla::ipc::TestShellCommandParent;
 using mozilla::ipc::PTestShellCommandParent;
+using mozilla::jsipc::PContextWrapperParent;
+using mozilla::jsipc::ContextWrapperParent;
 
 PTestShellCommandParent*
 TestShellParent::AllocPTestShellCommand(const nsString& aCommand)
 {
   return new TestShellCommandParent();
 }
 
 bool
@@ -61,16 +64,42 @@ TestShellParent::CommandDone(TestShellCo
 {
   // XXX what should happen if the callback fails?
   /*JSBool ok = */command->RunCallback(aResponse);
   command->ReleaseCallback();
 
   return true;
 }
 
+PContextWrapperParent*
+TestShellParent::AllocPContextWrapper()
+{
+    return new ContextWrapperParent();
+}
+
+bool
+TestShellParent::DeallocPContextWrapper(PContextWrapperParent* actor)
+{
+    delete actor;
+    return true;
+}
+
+bool
+TestShellParent::GetGlobalJSObject(JSContext* cx, JSObject** globalp)
+{
+    // TODO Unify this code with TabParent::GetGlobalJSObject.
+    nsTArray<PContextWrapperParent*> cwps(1);
+    ManagedPContextWrapperParent(cwps);
+    if (cwps.Length() < 1)
+        return false;
+    NS_ASSERTION(cwps.Length() == 1, "More than one PContextWrapper?");
+    ContextWrapperParent* cwp = static_cast<ContextWrapperParent*>(cwps[0]);
+    return (cwp->GetGlobalJSObject(cx, globalp));
+}
+
 JSBool
 TestShellCommandParent::SetCallback(JSContext* aCx,
                                     jsval aCallback)
 {
   if (!mCallback.Hold(aCx)) {
     return JS_FALSE;
   }
 
--- a/ipc/testshell/TestShellParent.h
+++ b/ipc/testshell/TestShellParent.h
@@ -42,33 +42,45 @@
 
 #include "mozilla/ipc/PTestShellParent.h"
 #include "mozilla/ipc/PTestShellCommandParent.h"
 
 #include "jsapi.h"
 #include "nsAutoJSValHolder.h"
 #include "nsStringGlue.h"
 
+struct JSContext;
+struct JSObject;
+
 namespace mozilla {
+
+namespace jsipc {
+class PContextWrapperParent;
+}
+
 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);
+
+  PContextWrapperParent* AllocPContextWrapper();
+  bool DeallocPContextWrapper(PContextWrapperParent* actor);
+
+  bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 };
 
 
 class TestShellCommandParent : public PTestShellCommandParent
 {
 public:
   TestShellCommandParent() : mCx(NULL) { }
 
--- a/ipc/testshell/XPCShellEnvironment.h
+++ b/ipc/testshell/XPCShellEnvironment.h
@@ -68,16 +68,20 @@ public:
     JSPrincipals* GetPrincipal() {
         return mJSPrincipals;
     }
 
     JSObject* GetGlobalObject() {
         return mGlobalHolder.ToJSObject();
     }
 
+    JSContext* GetContext() {
+        return mCx;
+    }
+
     void SetExitCode(int aExitCode) {
         mExitCode = aExitCode;
     }
     int ExitCode() {
         return mExitCode;
     }
 
     void SetIsQuitting() {
@@ -124,9 +128,9 @@ private:
     JSBool mQuitting;
     JSBool mReportWarnings;
     JSBool mCompileOnly;
 };
 
 } /* namespace ipc */
 } /* namespace mozilla */
 
-#endif /* _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ */
\ No newline at end of file
+#endif /* _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ */
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/CPOWTypes.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_jsipc_ContextWrapperTypes_h__
+#define mozilla_jsipc_ContextWrapperTypes_h__
+
+#include "jsapi.h"
+#include "jspubtd.h"
+
+namespace mozilla {
+namespace jsipc {
+
+using namespace IPC;
+
+struct void_t {};
+
+template <typename P>
+struct CPOWSingleton
+{
+    static void Write(Message*, const P&) {}
+    static bool Read(const Message*, void**, P*) { return true; }
+};
+
+template <typename Type, typename As>
+struct CPOWConvertible
+{
+    static void Write(Message* m, const Type& t) {
+        WriteParam(m, As(t));
+    }
+    static bool Read(const Message* m, void** iter, Type* tp) {
+        As a;
+        return (ReadParam(m, iter, &a) &&
+                (*tp = Type(a), true));
+    }
+}; 
+
+} // namespace jsipc
+} // namespace mozilla
+
+namespace IPC {
+
+using namespace mozilla::jsipc;
+
+template <> struct ParamTraits<void_t> : public CPOWSingleton<void_t> {};
+template <> struct ParamTraits<JSType> : public CPOWConvertible<JSType, int> {};
+
+}
+
+// TODO Use a more standard logging mechanism.
+#ifdef LOGGING
+#define CPOW_LOG(PRINTF_ARGS) \
+    JS_BEGIN_MACRO            \
+    printf("CPOW | ");        \
+    printf PRINTF_ARGS ;      \
+    printf("\n");             \
+    JS_END_MACRO
+#define JSVAL_TO_CSTR(CX, V) \
+    NS_ConvertUTF16toUTF8(nsString(JS_GetStringChars(JS_ValueToString(CX, V)))).get()
+#else
+#define CPOW_LOG(_) JS_BEGIN_MACRO JS_END_MACRO
+#define JSVAL_TO_CSTR(CX, V) ((char*)0)
+#endif
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ContextWrapperChild.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_jsipc_ContextWrapperChild_h__
+#define mozilla_jsipc_ContextWrapperChild_h__
+
+#include "mozilla/jsipc/PContextWrapperChild.h"
+#include "mozilla/jsipc/ObjectWrapperChild.h"
+
+#include "jsapi.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+
+namespace mozilla {
+namespace jsipc {
+
+class ContextWrapperChild
+    : public PContextWrapperChild
+{
+public:
+
+    ContextWrapperChild(JSContext* cx)
+        : mContext(cx)
+    {
+        mResidentObjectTable.Init();
+    }
+
+    JSContext* GetContext() { return mContext; }
+
+    PObjectWrapperChild* GetOrCreateWrapper(JSObject* obj,
+                                            bool makeGlobal = false)
+    {
+        if (!obj) // Don't wrap nothin'!
+            return NULL;
+        PObjectWrapperChild* wrapper;
+        while (!mResidentObjectTable.Get(obj, &wrapper)) {
+            wrapper = SendPObjectWrapperConstructor(AllocPObjectWrapper(obj),
+                                                    makeGlobal);
+            if (wrapper)
+                mResidentObjectTable.Put(obj, wrapper);
+            else
+                return NULL;
+        }
+        return wrapper;
+    }
+
+protected:
+
+    PObjectWrapperChild* AllocPObjectWrapper(JSObject* obj) {
+        return new ObjectWrapperChild(mContext, obj);
+    }
+    
+    PObjectWrapperChild* AllocPObjectWrapper(const bool&) {
+        return AllocPObjectWrapper(JS_GetGlobalObject(mContext));
+    }
+
+    bool DeallocPObjectWrapper(PObjectWrapperChild* actor) {
+        ObjectWrapperChild* owc = static_cast<ObjectWrapperChild*>(actor);
+        mResidentObjectTable.Remove(owc->GetJSObject());
+        return true;
+    }
+
+private:
+
+    JSContext* const mContext;
+
+    nsClassHashtable<nsPtrHashKey<JSObject>,
+                     PObjectWrapperChild> mResidentObjectTable;
+
+};
+
+}}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ContextWrapperParent.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_jsipc_ContextWrapperParent_h__
+#define mozilla_jsipc_ContextWrapperParent_h__
+
+#include "mozilla/jsipc/PContextWrapperParent.h"
+#include "mozilla/jsipc/ObjectWrapperParent.h"
+
+#include "jsapi.h"
+#include "nsAutoJSValHolder.h"
+
+namespace mozilla {
+namespace jsipc {
+    
+class ContextWrapperParent
+    : public PContextWrapperParent
+{
+public:
+
+    ContextWrapperParent() : mGlobal(NULL) {}
+
+    bool GetGlobalJSObject(JSContext* cx, JSObject** globalp) {
+        if (!mGlobal)
+            return false;
+        mGlobalHolder.Hold(cx);
+        mGlobalHolder = *globalp = mGlobal->GetJSObject(cx);
+        return true;
+    }
+
+    ObjectWrapperParent* GetGlobalObjectWrapper() const {
+        return mGlobal;
+    }
+
+private:
+
+    ObjectWrapperParent* mGlobal;
+    nsAutoJSValHolder mGlobalHolder;
+
+    PObjectWrapperParent* AllocPObjectWrapper(const bool&) {
+        return new ObjectWrapperParent();
+    }
+
+    bool RecvPObjectWrapperConstructor(PObjectWrapperParent* actor,
+                                       const bool& makeGlobal)
+    {
+        if (makeGlobal) {
+            mGlobalHolder.Release();
+            mGlobal = static_cast<ObjectWrapperParent*>(actor);
+        }
+        return true;
+    }
+
+    bool DeallocPObjectWrapper(PObjectWrapperParent* actor)
+    {
+        if (mGlobal &&
+            mGlobal == static_cast<ObjectWrapperParent*>(actor)) {
+            mGlobalHolder.Release();
+            mGlobal = NULL;
+        }
+        delete actor;
+        return true;
+    }
+
+};
+
+}}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/Makefile.in
@@ -0,0 +1,70 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = js
+LIBRARY_NAME = jsipc_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+EXPORT_LIBRARY = 1
+
+EXPORTS_NAMESPACES = mozilla/jsipc
+
+EXPORTS_mozilla/jsipc = \
+  CPOWTypes.h \
+  ContextWrapperChild.h \
+  ContextWrapperParent.h \
+  ObjectWrapperParent.h \
+  ObjectWrapperChild.h \
+  $(NULL)
+
+CPPSRCS = \
+  ObjectWrapperParent.cpp \
+  ObjectWrapperChild.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ObjectWrapperChild.cpp
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "base/basictypes.h"
+#include "jscntxt.h"
+
+#include "mozilla/jsipc/ContextWrapperChild.h"
+#include "mozilla/jsipc/ObjectWrapperChild.h"
+#include "mozilla/jsipc/CPOWTypes.h"
+
+#include "nsTArray.h"
+
+using namespace mozilla::jsipc;
+
+ObjectWrapperChild::ObjectWrapperChild(JSContext* cx, JSObject* obj)
+    : mObj(obj)
+{
+    JSAutoRequest request(cx);
+#ifdef DEBUG
+    bool added =
+#endif
+        JS_AddNamedRoot(cx, (void*)&mObj,
+                        "mozilla::jsipc::ObjectWrapperChild-rooted JSObject*");
+    NS_ASSERTION(added, "ObjectWrapperChild constructor failed to root JSObject*");
+}
+
+void
+ObjectWrapperChild::ActorDestroy(ActorDestroyReason why)
+{
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+    JS_RemoveRoot(cx, (void*)&mObj);
+}
+
+bool
+ObjectWrapperChild::JSObject_to_JSVariant(JSContext* cx, JSObject* from,
+                                          JSVariant* to)
+{
+    *to = Manager()->GetOrCreateWrapper(from);
+    return true;
+}
+
+bool
+ObjectWrapperChild::jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to)
+{
+    switch (JS_TypeOfValue(cx, from)) {
+    case JSTYPE_VOID:
+        *to = void_t();
+        return true;
+    case JSTYPE_NULL:
+        if (from != JSVAL_NULL)
+            return false;
+        // fall through
+    case JSTYPE_FUNCTION:
+        // fall through
+    case JSTYPE_OBJECT:
+        return JSObject_to_JSVariant(cx, JSVAL_TO_OBJECT(from), to);
+    case JSTYPE_STRING:
+        *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
+                                JS_GetStringLength(JSVAL_TO_STRING(from)));
+        return true;
+    case JSTYPE_NUMBER:
+        if (JSVAL_IS_INT(from))
+            *to = JSVAL_TO_INT(from);
+        else if (JSVAL_IS_DOUBLE(from))
+            *to = *JSVAL_TO_DOUBLE(from);
+        else return false;
+        return true;
+    case JSTYPE_BOOLEAN:
+        *to = !!JSVAL_TO_BOOLEAN(from);
+        return true;
+    case JSTYPE_XML:
+        // fall through
+    default:
+        return false;
+    }
+}
+
+/*static*/ bool
+ObjectWrapperChild::
+JSObject_from_PObjectWrapperChild(JSContext*,
+                                  const PObjectWrapperChild* from,
+                                  JSObject** to)
+{
+    const ObjectWrapperChild* owc =
+        static_cast<const ObjectWrapperChild*>(from);
+    *to = owc ? owc->mObj : JSVAL_NULL;
+    return true;
+}
+    
+/*static*/ bool
+ObjectWrapperChild::JSObject_from_JSVariant(JSContext* cx,
+                                            const JSVariant& from,
+                                            JSObject** to)
+{
+    if (from.type() != JSVariant::TPObjectWrapperChild)
+        return false;
+    return JSObject_from_PObjectWrapperChild(cx,
+                                             from.get_PObjectWrapperChild(),
+                                             to);
+}
+
+/*static*/ bool
+ObjectWrapperChild::jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
+                                         jsval* to)
+{
+    switch (from.type()) {
+    case JSVariant::Tvoid_t:
+        *to = JSVAL_VOID;
+        return true;
+    case JSVariant::TPObjectWrapperChild:
+        {
+            JSObject* obj;
+            if (!JSObject_from_JSVariant(cx, from, &obj))
+                return false;
+            *to = OBJECT_TO_JSVAL(obj);
+            return true;
+        }
+    case JSVariant::TnsString:
+        {
+            const nsString& str = from.get_nsString();
+            JSString* s = JS_NewUCStringCopyN(cx,
+                                              str.BeginReading(),
+                                              str.Length());
+            if (!s)
+                return false;
+            *to = STRING_TO_JSVAL(s);
+        }
+        return true;
+    case JSVariant::Tint:
+        *to = INT_TO_JSVAL(from.get_int());
+        return true;
+    case JSVariant::Tdouble:
+        return !!JS_NewDoubleValue(cx, from.get_double(), to);
+    case JSVariant::Tbool:
+        *to = BOOLEAN_TO_JSVAL(from.get_bool());
+        return true;
+    default:
+        return false;
+    }
+}
+    
+ContextWrapperChild*
+ObjectWrapperChild::Manager()
+{
+    PContextWrapperChild* pcwc = PObjectWrapperChild::Manager();
+    return static_cast<ContextWrapperChild*>(pcwc);
+}
+
+static bool
+jsid_to_nsString(JSContext* cx, jsid from, nsString* to)
+{
+    jsval v;
+    if (JS_IdToValue(cx, from, &v) && JSVAL_IS_STRING(v)) {
+        *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(v)),
+                                JS_GetStringLength(JSVAL_TO_STRING(v)));
+        return true;
+    }
+    return false;
+}
+    
+static bool
+jsid_from_nsString(JSContext* cx, const nsString& from, jsid* to)
+{
+    JSString* str = JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length());
+    if (!str)
+        return false;
+    return JS_ValueToId(cx, STRING_TO_JSVAL(str), to);
+}
+
+#if 0
+// The general schema for ObjectWrapperChild::Answer* methods:
+bool
+ObjectWrapperChild::AnswerSomething(/* in-parameters */
+                                    /* out-parameters */)
+{
+    // initialize out-parameters for failure
+    JSAutoRequest request(Manager()->GetContext());
+    // validate in-parameters, else return false
+    // successfully perform local JS operations, else return true
+    // perform out-parameter conversions, else return false
+    return true;
+}
+// There's an important subtlety here: though a local JS operation may
+// fail, leaving out-parameters uninitialized, we must initialize all
+// out-parameters when reporting success (returning true) to the IPC
+// messaging system.  See AnswerGetProperty for illustration.
+#endif
+
+bool
+ObjectWrapperChild::AnswerAddProperty(const nsString& id,
+                                      JSBool* ok)
+{
+    jsid interned_id;
+
+    *ok = JS_FALSE;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!jsid_from_nsString(cx, id, &interned_id))
+        return false;
+    
+    *ok = JS_DefinePropertyById(cx, mObj, interned_id, JSVAL_VOID,
+                                NULL, NULL, 0);
+    return true;
+}
+
+bool
+ObjectWrapperChild::AnswerGetProperty(const nsString& id,
+                                      JSBool* ok, JSVariant* vp)
+{
+    jsid interned_id;
+    jsval val;
+
+    *ok = JS_FALSE;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!jsid_from_nsString(cx, id, &interned_id))
+        return false;
+
+    *ok = JS_GetPropertyById(cx, mObj, interned_id, &val);
+
+    // Since we fully expect this call to jsval_to_JSVariant to return
+    // true, we can't just leave vp uninitialized when JS_GetPropertyById
+    // returns JS_FALSE.  This pitfall could be avoided in general if IPDL
+    // ensured that outparams were pre-initialized to some default value
+    // (XXXfixme cjones?).
+    return jsval_to_JSVariant(cx, *ok ? val : JSVAL_VOID, vp);
+}
+
+bool
+ObjectWrapperChild::AnswerSetProperty(const nsString& id, const JSVariant& v,
+                                      JSBool* ok, JSVariant* vp)
+{
+    jsid interned_id;
+    jsval val;
+
+    *ok = JS_FALSE;
+    *vp = v;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!jsid_from_nsString(cx, id, &interned_id) ||
+        !jsval_from_JSVariant(cx, v, &val))
+        return false;
+
+    *ok = JS_SetPropertyById(cx, mObj, interned_id, &val);
+
+    return jsval_to_JSVariant(cx, *ok ? val : JSVAL_VOID, vp);
+}
+
+bool
+ObjectWrapperChild::AnswerDelProperty(const nsString& id,
+                                      JSBool* ok, JSVariant* vp)
+{
+    jsid interned_id;
+    jsval val;
+
+    *ok = JS_FALSE;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!jsid_from_nsString(cx, id, &interned_id))
+        return false;
+
+    *ok = JS_DeletePropertyById2(cx, mObj, interned_id, &val);
+
+    return jsval_to_JSVariant(cx, *ok ? val : JSVAL_VOID, vp);
+}
+
+static const PRUint32 sNextIdIndexSlot = 0;
+static const PRUint32 sNumNewEnumerateStateSlots = 1;
+
+static void
+CPOW_NewEnumerateState_Finalize(JSContext* cx, JSObject* state)
+{
+    nsTArray<nsString>* strIds =
+        static_cast<nsTArray<nsString>*>(JS_GetPrivate(cx, state));
+
+    if (strIds) {
+        delete strIds;
+        JS_SetPrivate(cx, state, NULL);
+    }
+}
+
+// Similar to IteratorClass in XPCWrapper.cpp
+static const JSClass sCPOW_NewEnumerateState_JSClass = {
+    "CPOW NewEnumerate State",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(sNumNewEnumerateStateSlots),
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_EnumerateStub, JS_ResolveStub,
+    JS_ConvertStub,   CPOW_NewEnumerateState_Finalize,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+bool
+ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */
+                                           JSBool* ok, JSVariant* statep, int* idp)
+{
+    *ok = JS_FALSE;
+    *idp = 0;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    JSClass* clasp = const_cast<JSClass*>(&sCPOW_NewEnumerateState_JSClass);
+    JSObject* state = JS_NewObjectWithGivenProto(cx, clasp, NULL, NULL);
+    if (!state)
+        return false;
+    JSAutoTempValueRooter tvr(cx, state);
+    
+    for (JSObject* proto = mObj;
+         proto;
+         proto = JS_GetPrototype(cx, proto))
+    {
+        JSAutoIdArray ids(cx, JS_Enumerate(cx, proto));
+        for (uint i = 0; i < ids.length(); ++i)
+            JS_DefinePropertyById(cx, state, ids[i], JSVAL_VOID,
+                                  NULL, NULL, JSPROP_ENUMERATE | JSPROP_SHARED);
+    }
+
+    nsTArray<nsString>* strIds;
+    {
+        JSAutoIdArray ids(cx, JS_Enumerate(cx, state));
+        if (!ids)
+            return false;
+        strIds = new nsTArray<nsString>(ids.length());
+        for (uint i = 0; i < ids.length(); ++i)
+            if (!jsid_to_nsString(cx, ids[i], strIds->AppendElement())) {
+                delete strIds;
+                return false;
+            }
+    }
+    *idp = strIds->Length();
+
+    *ok = (JS_SetPrivate(cx, state, strIds) &&
+           JS_SetReservedSlot(cx, state, sNextIdIndexSlot,
+                              JSVAL_ZERO) &&
+           JSObject_to_JSVariant(cx, state, statep));
+
+    return true;
+}
+
+bool
+ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state,
+                                           JSBool* ok, JSVariant* statep, nsString* idp)
+{
+    JSObject* state;
+    jsval v;
+
+    *ok = JS_FALSE;
+    *statep = in_state;
+    idp->Truncate();
+    
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!JSObject_from_JSVariant(cx, in_state, &state))
+        return false;
+
+    nsTArray<nsString>* strIds =
+        static_cast<nsTArray<nsString>*>(JS_GetPrivate(cx, state));
+
+    if (!strIds || !JS_GetReservedSlot(cx, state, sNextIdIndexSlot, &v))
+        return false;
+
+    jsint i = JSVAL_TO_INT(v);
+    NS_ASSERTION(i >= 0, "Index of next jsid negative?");
+    NS_ASSERTION(i <= strIds->Length(), "Index of next jsid too large?");
+
+    if (i == strIds->Length()) {
+        *ok = JS_TRUE;
+        return JSObject_to_JSVariant(cx, NULL, statep);
+    }
+
+    *idp = strIds->ElementAt(i);
+    *ok = JS_SetReservedSlot(cx, state, sNextIdIndexSlot,
+                             INT_TO_JSVAL(i + 1));
+    return true;
+}
+    
+bool
+ObjectWrapperChild::RecvNewEnumerateDestroy(const JSVariant& in_state)
+{
+    JSObject* state;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!JSObject_from_JSVariant(cx, in_state, &state))
+        return false;
+
+    CPOW_NewEnumerateState_Finalize(cx, state);
+
+    return true;
+}
+
+bool
+ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags,
+                                     JSBool* ok, PObjectWrapperChild** obj2)
+{
+    jsid interned_id;
+    
+    *ok = JS_FALSE;
+    *obj2 = NULL;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    if (!jsid_from_nsString(cx, id, &interned_id))
+        return false;
+
+    CPOW_LOG(("new-resolving \"%s\"...",
+              NS_ConvertUTF16toUTF8(id).get()));
+
+    JSPropertyDescriptor desc;
+    if (!JS_GetPropertyDescriptorById(cx, mObj, interned_id, flags, &desc))
+        return true;
+
+    *ok = JS_TRUE;
+
+    if (desc.obj)
+        *obj2 = Manager()->GetOrCreateWrapper(desc.obj);
+
+    return true;
+}
+
+bool
+ObjectWrapperChild::AnswerConvert(const JSType& type,
+                                  JSBool* ok, JSVariant* vp)
+{
+    jsval v;
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+    *ok = JS_ConvertValue(cx, OBJECT_TO_JSVAL(mObj), type, &v);
+    return jsval_to_JSVariant(cx, *ok ? v : JSVAL_VOID, vp);
+}
+
+namespace {
+    // Should be an overestimate of typical JS function arity.
+    typedef nsAutoTArray<jsval, 5> AutoJSArgs;
+}
+
+bool
+ObjectWrapperChild::AnswerCall(PObjectWrapperChild* receiver, const nsTArray<JSVariant>& argv,
+                               JSBool* ok, JSVariant* rval)
+{
+    *ok = JS_FALSE;
+    
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    JSObject* obj;
+    if (!JSObject_from_PObjectWrapperChild(cx, receiver, &obj))
+        return false;
+
+    AutoJSArgs args;
+    PRUint32 argc = argv.Length();
+    jsval *jsargs = args.AppendElements(argc);
+    if (!jsargs)
+        return false;
+    JSAutoTempValueRooter tvr(cx, argc, jsargs);
+
+    for (PRUint32 i = 0; i < argc; ++i)
+        if (!jsval_from_JSVariant(cx, argv.ElementAt(i), jsargs + i))
+            return false;
+
+    jsval rv;
+    *ok = JS_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(mObj),
+                               argv.Length(), jsargs, &rv);
+
+    return jsval_to_JSVariant(cx, *ok ? rv : JSVAL_VOID, rval);
+}
+
+bool
+ObjectWrapperChild::AnswerConstruct(const nsTArray<JSVariant>& argv,
+                                    JSBool* ok, PObjectWrapperChild** rval)
+{
+    *ok = JS_FALSE;
+
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+
+    AutoJSArgs args;
+    PRUint32 argc = argv.Length();
+    jsval* jsargs = args.AppendElements(argc);
+    if (!jsargs)
+        return false;
+    JSAutoTempValueRooter tvr(cx, argc, jsargs);
+
+    for (PRUint32 i = 0; i < argc; ++i)
+        if (!jsval_from_JSVariant(cx, argv.ElementAt(i), jsargs + i))
+            return false;
+
+    JSObject* obj = JS_New(cx, mObj, argc, jsargs);
+
+    *ok = !!obj;
+    *rval = Manager()->GetOrCreateWrapper(obj);
+
+    return true;
+}
+
+bool
+ObjectWrapperChild::AnswerHasInstance(const JSVariant& v,
+                                      JSBool* ok, JSBool* bp)
+{
+    jsval candidate;
+    JSContext* cx = Manager()->GetContext();
+    JSAutoRequest request(cx);
+    if (!jsval_from_JSVariant(cx, v, &candidate))
+        return false;
+    *ok = JS_HasInstance(cx, mObj, candidate, bp);
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ObjectWrapperChild.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_jsipc_ObjectWrapperChild_h__
+#define mozilla_jsipc_ObjectWrapperChild_h__
+
+#include "mozilla/jsipc/PObjectWrapperChild.h"
+
+using mozilla::jsipc::JSVariant;
+
+namespace mozilla {
+namespace jsipc {
+
+class ContextWrapperChild;
+  
+class ObjectWrapperChild
+    : public PObjectWrapperChild
+{
+public:
+
+    ObjectWrapperChild(JSContext* cx, JSObject* obj);
+
+    JSObject* GetJSObject() const { return mObj; }
+    
+private:
+
+    JSObject* const mObj;
+
+    bool JSObject_to_JSVariant(JSContext* cx, JSObject* from, JSVariant* to);
+    bool jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to);
+
+    static bool JSObject_from_PObjectWrapperChild(JSContext* cx,
+                                                  const PObjectWrapperChild* from,
+                                                  JSObject** to);
+    static bool JSObject_from_JSVariant(JSContext* cx, const JSVariant& from,
+                                        JSObject** to);
+    static bool jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
+                                     jsval* to);
+
+    ContextWrapperChild* Manager();
+
+protected:
+
+    void ActorDestroy(ActorDestroyReason why);
+
+    bool AnswerAddProperty(const nsString& id,
+                           JSBool* ok);
+
+    bool AnswerGetProperty(const nsString& id,
+                           JSBool* ok, JSVariant* vp);
+
+    bool AnswerSetProperty(const nsString& id, const JSVariant& v,
+                           JSBool* ok, JSVariant* vp);
+
+    bool AnswerDelProperty(const nsString& id,
+                           JSBool* ok, JSVariant* vp);
+
+    bool AnswerNewEnumerateInit(/* no in-parameters */
+                                JSBool* ok, JSVariant* statep, int* idp);
+
+    bool AnswerNewEnumerateNext(const JSVariant& in_state,
+                                JSBool* ok, JSVariant* statep, nsString* idp);
+
+    bool RecvNewEnumerateDestroy(const JSVariant& in_state);
+
+    bool AnswerNewResolve(const nsString& id, const int& flags,
+                          JSBool* ok, PObjectWrapperChild** obj2);
+
+    bool AnswerConvert(const JSType& type,
+                       JSBool* ok, JSVariant* vp);
+
+    bool AnswerCall(PObjectWrapperChild* receiver, const nsTArray<JSVariant>& argv,
+                    JSBool* ok, JSVariant* rval);
+
+    bool AnswerConstruct(const nsTArray<JSVariant>& argv,
+                         JSBool* ok, PObjectWrapperChild** rval);
+
+    bool AnswerHasInstance(const JSVariant& v,
+                           JSBool* ok, JSBool* bp);
+};
+
+}}
+  
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ObjectWrapperParent.cpp
@@ -0,0 +1,659 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/jsipc/ObjectWrapperParent.h"
+#include "mozilla/jsipc/ContextWrapperParent.h"
+#include "mozilla/jsipc/CPOWTypes.h"
+
+#include "jsobj.h"
+#include "jsfun.h"
+#include "jsutil.h"
+
+using namespace mozilla::jsipc;
+
+namespace {
+
+    // Only need one reserved slot because the ObjectWrapperParent* is
+    // stored in the private slot.
+    static const uintN sFlagsSlot = 0;
+    static const uintN sNumSlots = 1;
+    static const uintN CPOW_FLAG_RESOLVING = 1 << 0;
+
+    class AutoResolveFlag
+    {
+        JSContext* mContext;
+        JSObject* mObj;
+        uintN mOldFlags;
+        JS_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+        static uintN GetFlags(JSContext* cx, JSObject* obj) {
+            jsval v;
+#ifdef DEBUG
+            JSBool ok =
+#endif
+                JS_GetReservedSlot(cx, obj, sFlagsSlot, &v);
+            NS_ASSERTION(ok, "Failed to get CPOW flags");
+            return JSVAL_TO_INT(v);
+        }
+
+        static uintN SetFlags(JSContext* cx, JSObject* obj, uintN flags) {
+            uintN oldFlags = GetFlags(cx, obj);
+            if (oldFlags != flags)
+                JS_SetReservedSlot(cx, obj, sFlagsSlot, INT_TO_JSVAL(flags));
+            return oldFlags;
+        }
+
+    public:
+
+        AutoResolveFlag(JSContext* cx,
+                        JSObject* obj
+                        JS_GUARD_OBJECT_NOTIFIER_PARAM)
+            : mContext(cx)
+            , mObj(obj)
+            , mOldFlags(SetFlags(cx, obj,
+                                 GetFlags(cx, obj) | CPOW_FLAG_RESOLVING))
+        {
+            JS_GUARD_OBJECT_NOTIFIER_INIT;
+        }
+
+        ~AutoResolveFlag() {
+            SetFlags(mContext, mObj, mOldFlags);
+        }
+
+        static JSBool IsSet(JSContext* cx, JSObject* obj) {
+            return GetFlags(cx, obj) & CPOW_FLAG_RESOLVING;
+        }
+
+    };
+
+}
+
+const JSExtendedClass ObjectWrapperParent::sCPOW_JSClass = {
+    // JSClass (JSExtendedClass.base) initialization
+    { "CrossProcessObjectWrapper",
+      JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_IS_EXTENDED |
+      JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(sNumSlots),
+      ObjectWrapperParent::CPOW_AddProperty,
+      ObjectWrapperParent::CPOW_DelProperty,
+      ObjectWrapperParent::CPOW_GetProperty,
+      ObjectWrapperParent::CPOW_SetProperty,
+      (JSEnumerateOp) ObjectWrapperParent::CPOW_NewEnumerate,
+        (JSResolveOp) ObjectWrapperParent::CPOW_NewResolve,
+      ObjectWrapperParent::CPOW_Convert,
+      ObjectWrapperParent::CPOW_Finalize,
+      nsnull, // getObjectOps
+      nsnull, // checkAccess
+      ObjectWrapperParent::CPOW_Call,
+      ObjectWrapperParent::CPOW_Construct,
+      nsnull, // xdrObject
+      ObjectWrapperParent::CPOW_HasInstance,
+      nsnull, // mark
+      nsnull, // reserveSlots
+    },
+
+    // JSExtendedClass initialization
+    ObjectWrapperParent::CPOW_Equality,
+    nsnull, // outerObject
+    nsnull, // innerObject
+    nsnull, // iterator
+    nsnull, // wrappedObject
+    JSCLASS_NO_RESERVED_MEMBERS
+};
+
+void
+ObjectWrapperParent::ActorDestroy(ActorDestroyReason)
+{
+    if (mObj)
+        mObj->setPrivate(NULL);
+}
+    
+JSObject*
+ObjectWrapperParent::GetJSObject(JSContext* cx) const
+{
+    JSClass* clasp = const_cast<JSClass*>(&ObjectWrapperParent::sCPOW_JSClass.base);
+    if (!mObj && (mObj = JS_NewObject(cx, clasp, NULL, NULL))) {
+        JS_SetPrivate(cx, mObj, (void*)this);
+        JS_SetReservedSlot(cx, mObj, sFlagsSlot, JSVAL_ZERO);
+    }
+    return mObj;
+}
+
+static ObjectWrapperParent*
+Unwrap(JSContext* cx, JSObject* obj)
+{
+    while (STOBJ_GET_CLASS(obj) != &ObjectWrapperParent::sCPOW_JSClass.base)
+        if (!(obj = STOBJ_GET_PROTO(obj)))
+            return NULL;
+    
+    ObjectWrapperParent* self =
+        static_cast<ObjectWrapperParent*>(JS_GetPrivate(cx, obj));
+
+    NS_ASSERTION(!self || self->GetJSObject(cx) == obj,
+                 "Wrapper and wrapped object disagree?");
+    
+    return self;
+}
+
+/*static*/ bool
+ObjectWrapperParent::jsval_to_JSVariant(JSContext* cx, jsval from,
+                                        JSVariant* to)
+{
+    switch (JS_TypeOfValue(cx, from)) {
+    case JSTYPE_VOID:
+        *to = void_t();
+        return true;
+    case JSTYPE_NULL:
+        if (from != JSVAL_NULL)
+            return false;
+        // fall through
+    case JSTYPE_FUNCTION:
+        // CPOWs can fool JS_TypeOfValue into returning JSTYPE_FUNCTION
+        // because they have a call hook, but CPOWs are really objects, so
+        // fall through to the JSTYPE_OBJECT case:
+    case JSTYPE_OBJECT:
+        {
+            PObjectWrapperParent* powp;
+            if (!JSObject_to_PObjectWrapperParent(cx, JSVAL_TO_OBJECT(from), &powp))
+                return false;
+            *to = powp;
+        }
+        return true;
+    case JSTYPE_STRING:
+        *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
+                                JS_GetStringLength(JSVAL_TO_STRING(from)));
+        return true;
+    case JSTYPE_NUMBER:
+        if (JSVAL_IS_INT(from))
+            *to = JSVAL_TO_INT(from);
+        else if (JSVAL_IS_DOUBLE(from))
+            *to = *JSVAL_TO_DOUBLE(from);
+        else return false;
+        return true;
+    case JSTYPE_BOOLEAN:
+        *to = !!JSVAL_TO_BOOLEAN(from);
+        return true;
+    case JSTYPE_XML:
+    default:
+        return false;
+    }
+}
+
+/*static*/ bool
+ObjectWrapperParent::jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
+                                          jsval* to)
+{
+    switch (from.type()) {
+    case JSVariant::Tvoid_t:
+        *to = JSVAL_VOID;
+        return true;
+    case JSVariant::TPObjectWrapperParent:
+        return jsval_from_PObjectWrapperParent(cx, from.get_PObjectWrapperParent(), to);
+    case JSVariant::TnsString:
+        {
+            JSString* str = JS_NewUCStringCopyZ(cx, from.get_nsString().BeginReading());
+            if (!str)
+                return false;
+            *to = STRING_TO_JSVAL(str);
+            return true;
+        }
+    case JSVariant::Tint:
+        *to = INT_TO_JSVAL(from.get_int());
+        return true;
+    case JSVariant::Tdouble:
+        return !!JS_NewDoubleValue(cx, from.get_double(), to);
+    case JSVariant::Tbool:
+        *to = BOOLEAN_TO_JSVAL(from.get_bool());
+        return true;
+    default:
+        return false;
+    }
+}
+
+/*static*/ bool
+ObjectWrapperParent::
+JSObject_to_PObjectWrapperParent(JSContext* cx, JSObject* from,
+                                 PObjectWrapperParent** to)
+{
+    if (!from) {
+        *to = NULL;
+        return true;
+    }
+    ObjectWrapperParent* owp = Unwrap(cx, from);
+    if (!owp)
+        return false;
+    *to = owp;
+    return true;
+}
+
+/*static*/ bool
+ObjectWrapperParent::
+JSObject_from_PObjectWrapperParent(JSContext* cx,
+                                   const PObjectWrapperParent* from,
+                                   JSObject** to)
+{
+    const ObjectWrapperParent* owp =
+        static_cast<const ObjectWrapperParent*>(from);
+    *to = owp
+        ? owp->GetJSObject(cx)
+        : JSVAL_TO_OBJECT(JSVAL_NULL);
+    return true;
+}
+
+/*static*/ bool
+ObjectWrapperParent::
+jsval_from_PObjectWrapperParent(JSContext* cx,
+                                const PObjectWrapperParent* from,
+                                jsval* to)
+{
+    JSObject* obj;
+    if (!JSObject_from_PObjectWrapperParent(cx, from, &obj))
+        return false;
+    *to = OBJECT_TO_JSVAL(obj);
+    return true;
+}
+    
+static bool
+jsid_from_int(JSContext* cx, int from, jsid* to)
+{
+    jsval v = INT_TO_JSVAL(from);
+    return JS_ValueToId(cx, v, to);
+}
+
+static bool
+jsid_from_nsString(JSContext* cx, const nsString& from, jsid* to)
+{
+    JSString* str = JS_NewUCStringCopyZ(cx, from.BeginReading());
+    if (!str)
+        return false;
+    return JS_ValueToId(cx, STRING_TO_JSVAL(str), to);
+}
+
+static bool
+jsval_to_nsString(JSContext* cx, jsval from, nsString* to)
+{
+    JSString* str;
+    if ((str = JS_ValueToString(cx, from))) {
+        *to = JS_GetStringChars(str);
+        return true;
+    }
+    return false;
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_AddProperty(JSContext *cx, JSObject *obj, jsval id,
+                                      jsval *vp)
+{
+    CPOW_LOG(("Calling CPOW_AddProperty (%s)...",
+              JSVAL_TO_CSTR(cx, id)));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    if (AutoResolveFlag::IsSet(cx, obj))
+        return JS_TRUE;
+    
+    nsString in_id;
+
+    if (!jsval_to_nsString(cx, id, &in_id))
+        return JS_FALSE;
+
+    JSBool out_ok;
+
+    return (self->CallAddProperty(in_id,
+                                  &out_ok) &&
+            out_ok);
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_GetProperty(JSContext *cx, JSObject *obj, jsval id,
+                                      jsval *vp)
+{
+    CPOW_LOG(("Calling CPOW_GetProperty (%s)...",
+              JSVAL_TO_CSTR(cx, id)));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    nsString in_id;
+
+    if (!jsval_to_nsString(cx, id, &in_id))
+        return JS_FALSE;
+
+    JSBool out_ok;
+    JSVariant out_v;
+    
+    return (self->CallGetProperty(in_id,
+                                  &out_ok, &out_v) &&
+            out_ok &&
+            self->jsval_from_JSVariant(cx, out_v, vp));
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_SetProperty(JSContext *cx, JSObject *obj, jsval id,
+                                      jsval *vp)
+{
+    CPOW_LOG(("Calling CPOW_SetProperty (%s)...",
+              JSVAL_TO_CSTR(cx, id)));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    nsString in_id;
+    JSVariant in_v;
+
+    if (!jsval_to_nsString(cx, id, &in_id) ||
+        !self->jsval_to_JSVariant(cx, *vp, &in_v))
+        return JS_FALSE;
+    
+    JSBool out_ok;
+    JSVariant out_v;
+
+    return (self->CallSetProperty(in_id, in_v,
+                                  &out_ok, &out_v) &&
+            out_ok &&
+            self->jsval_from_JSVariant(cx, out_v, vp));
+}    
+    
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_DelProperty(JSContext *cx, JSObject *obj, jsval id,
+                                      jsval *vp)
+{
+    CPOW_LOG(("Calling CPOW_DelProperty (%s)...",
+              JSVAL_TO_CSTR(cx, id)));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    nsString in_id;
+
+    if (!jsval_to_nsString(cx, id, &in_id))
+        return JS_FALSE;
+
+    JSBool out_ok;
+    JSVariant out_v;
+    
+    return (self->CallDelProperty(in_id,
+                                  &out_ok, &out_v) &&
+            out_ok &&
+            jsval_from_JSVariant(cx, out_v, vp));
+}
+
+JSBool
+ObjectWrapperParent::NewEnumerateInit(JSContext* cx, jsval* statep, jsid* idp)
+{
+    JSBool out_ok;
+    JSVariant out_state;
+    int out_id;
+
+    return (CallNewEnumerateInit(&out_ok, &out_state, &out_id) &&
+            out_ok &&
+            jsval_from_JSVariant(cx, out_state, statep) &&
+            (!idp || jsid_from_int(cx, out_id, idp)));
+}
+
+JSBool
+ObjectWrapperParent::NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp)
+{
+    JSVariant in_state;
+    if (!jsval_to_JSVariant(cx, *statep, &in_state))
+        return JS_FALSE;
+
+    JSBool out_ok;
+    JSVariant out_state;
+    nsString out_id;
+
+    if (CallNewEnumerateNext(in_state,
+                             &out_ok, &out_state, &out_id) &&
+        out_ok &&
+        jsval_from_JSVariant(cx, out_state, statep) &&
+        jsid_from_nsString(cx, out_id, idp))
+    {
+        JSObject* obj = GetJSObject(cx);
+        AutoResolveFlag arf(cx, obj);
+        return JS_DefinePropertyById(cx, obj, *idp, JSVAL_VOID, NULL, NULL,
+                                     JSPROP_ENUMERATE);
+    }
+    return JS_FALSE;
+}
+
+JSBool
+ObjectWrapperParent::NewEnumerateDestroy(JSContext* cx, jsval state)
+{
+    JSVariant in_state;
+    if (!jsval_to_JSVariant(cx, state, &in_state))
+        return JS_FALSE;
+
+    return SendNewEnumerateDestroy(in_state);
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_NewEnumerate(JSContext *cx, JSObject *obj,
+                                       JSIterateOp enum_op, jsval *statep,
+                                       jsid *idp)
+{
+    CPOW_LOG(("Calling CPOW_NewEnumerate..."));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    switch (enum_op) {
+    case JSENUMERATE_INIT:
+        return self->NewEnumerateInit(cx, statep, idp);
+    case JSENUMERATE_NEXT:
+        return self->NewEnumerateNext(cx, statep, idp);
+    case JSENUMERATE_DESTROY:
+        return self->NewEnumerateDestroy(cx, *statep);
+    }
+
+    return JS_FALSE;
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_NewResolve(JSContext *cx, JSObject *obj, jsval id,
+                                     uintN flags, JSObject **objp)
+{
+    CPOW_LOG(("Calling CPOW_NewResolve (%s)...",
+              JSVAL_TO_CSTR(cx, id)));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    nsString in_id;
+
+    if (!jsval_to_nsString(cx, id, &in_id))
+        return JS_FALSE;
+
+    JSBool out_ok;
+    PObjectWrapperParent* out_pobj;
+
+    if (!self->CallNewResolve(in_id, flags,
+                              &out_ok, &out_pobj) ||
+        !out_ok ||
+        !JSObject_from_PObjectWrapperParent(cx, out_pobj, objp))
+        return JS_FALSE;
+
+    jsid interned_id;
+    if (*objp &&
+        JS_ValueToId(cx, id, &interned_id)) {
+        AutoResolveFlag arf(cx, *objp);
+        JS_DefinePropertyById(cx, *objp, interned_id, JSVAL_VOID, NULL, NULL,
+                              JSPROP_ENUMERATE);
+    }
+    return JS_TRUE;
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_Convert(JSContext *cx, JSObject *obj, JSType type,
+                                  jsval *vp)
+{
+    CPOW_LOG(("Calling CPOW_Convert (to %s)...",
+              JS_GetTypeName(cx, type)));
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    *vp = OBJECT_TO_JSVAL(obj);
+
+    return JS_TRUE;
+}
+
+/*static*/ void
+ObjectWrapperParent::CPOW_Finalize(JSContext* cx, JSObject* obj)
+{
+    CPOW_LOG(("Calling CPOW_Finalize..."));
+    
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (self)
+        ObjectWrapperParent::Send__delete__(self);
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_Call(JSContext* cx, JSObject* obj, uintN argc,
+                               jsval* argv, jsval* rval)
+{
+    CPOW_LOG(("Calling CPOW_Call..."));
+
+    ObjectWrapperParent* function =
+        Unwrap(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)));
+    if (!function)
+        return JS_FALSE;
+
+    ObjectWrapperParent* receiver = Unwrap(cx, obj);
+    if (!receiver) {
+        // Substitute child global for parent global object.
+        // TODO First make sure we're really replacing the global object?
+        ContextWrapperParent* manager =
+            static_cast<ContextWrapperParent*>(function->Manager());
+        receiver = manager->GetGlobalObjectWrapper();
+    }
+
+    nsTArray<JSVariant> in_argv(argc);
+    for (uintN i = 0; i < argc; i++)
+        if (!jsval_to_JSVariant(cx, argv[i], in_argv.AppendElement()))
+            return JS_FALSE;
+    
+    JSBool out_ok;
+    JSVariant out_rval;
+
+    return (function->CallCall(receiver, in_argv,
+                               &out_ok, &out_rval) &&
+            out_ok &&
+            jsval_from_JSVariant(cx, out_rval, rval));
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_Construct(JSContext *cx, JSObject *obj, uintN argc,
+                                    jsval *argv, jsval *rval)
+{
+    CPOW_LOG(("Calling CPOW_Construct..."));
+    
+    ObjectWrapperParent* constructor =
+        Unwrap(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)));
+    if (!constructor)
+        return JS_FALSE;
+
+    nsTArray<JSVariant> in_argv(argc);
+    for (uintN i = 0; i < argc; i++)
+        if (!jsval_to_JSVariant(cx, argv[i], in_argv.AppendElement()))
+            return JS_FALSE;
+
+    JSBool out_ok;
+    PObjectWrapperParent* out_powp;
+
+    return (constructor->CallConstruct(in_argv,
+                                       &out_ok, &out_powp) &&
+            out_ok &&
+            jsval_from_PObjectWrapperParent(cx, out_powp, rval));
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_HasInstance(JSContext *cx, JSObject *obj, jsval v,
+                                      JSBool *bp)
+{
+    CPOW_LOG(("Calling CPOW_HasInstance..."));
+
+    *bp = JS_FALSE;
+
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    JSVariant in_v;
+
+    if (!jsval_to_JSVariant(cx, v, &in_v))
+        return JS_FALSE;
+
+    JSBool out_ok;
+
+    return (self->CallHasInstance(in_v,
+                                  &out_ok, bp) &&
+            out_ok);
+}
+
+/*static*/ JSBool
+ObjectWrapperParent::CPOW_Equality(JSContext *cx, JSObject *obj, jsval v,
+                                   JSBool *bp)
+{
+    CPOW_LOG(("Calling CPOW_Equality..."));
+
+    *bp = JS_FALSE;
+    
+    ObjectWrapperParent* self = Unwrap(cx, obj);
+    if (!self)
+        return JS_FALSE;
+
+    if (JSVAL_IS_PRIMITIVE(v))
+        return JS_TRUE;
+
+    ObjectWrapperParent* other = Unwrap(cx, JSVAL_TO_OBJECT(v));
+    if (!other)
+        return JS_TRUE;
+
+    *bp = (self == other);
+    
+    return JS_TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ObjectWrapperParent.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_jsipc_ObjectWrapperParent_h__
+#define mozilla_jsipc_ObjectWrapperParent_h__
+
+#include "mozilla/jsipc/PObjectWrapperParent.h"
+#include "jsapi.h"
+#include "nsAutoJSValHolder.h"
+
+namespace mozilla {
+namespace jsipc {
+
+class ObjectWrapperParent
+    : public PObjectWrapperParent
+{
+public:
+
+    ObjectWrapperParent()
+        : mObj(NULL)
+    {}
+
+    JSObject* GetJSObject(JSContext* cx) const;
+
+    jsval GetJSVal(JSContext* cx) const {
+        return OBJECT_TO_JSVAL(GetJSObject(cx));
+    }
+
+    static const JSExtendedClass sCPOW_JSClass;
+
+protected:
+
+    void ActorDestroy(ActorDestroyReason why);
+
+private:
+
+    mutable JSObject* mObj;
+
+    static JSBool
+    CPOW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+
+    static JSBool
+    CPOW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+
+    static JSBool
+    CPOW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    
+    static JSBool
+    CPOW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+
+    JSBool NewEnumerateInit(JSContext* cx, jsval* statep, jsid* idp);
+    JSBool NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp);
+    JSBool NewEnumerateDestroy(JSContext* cx, jsval state);
+    static JSBool
+    CPOW_NewEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
+                      jsval *statep, jsid *idp);
+
+    static JSBool
+    CPOW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
+                    JSObject **objp);
+
+    static JSBool
+    CPOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
+
+    static void
+    CPOW_Finalize(JSContext* cx, JSObject* obj);
+
+    static JSBool
+    CPOW_Call(JSContext* cx, JSObject* obj, uintN argc, jsval* argv,
+              jsval* rval);
+
+    static JSBool
+    CPOW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+                   jsval *rval);
+    
+    static JSBool
+    CPOW_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
+
+    static JSBool
+    CPOW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
+
+    static bool jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to);
+    static bool jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
+                                     jsval* to);
+    static bool
+    JSObject_to_PObjectWrapperParent(JSContext* cx, JSObject* from,
+                                     PObjectWrapperParent** to);
+    static bool
+    JSObject_from_PObjectWrapperParent(JSContext* cx,
+                                       const PObjectWrapperParent* from,
+                                       JSObject** to);
+    static bool
+    jsval_from_PObjectWrapperParent(JSContext* cx,
+                                    const PObjectWrapperParent* from,
+                                    jsval* to);
+};
+
+}}
+  
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/PContextWrapper.ipdl
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PIFrameEmbedding.ipdl";
+include protocol "PTestShell.ipdl";
+include protocol "PObjectWrapper.ipdl";
+
+namespace mozilla {
+namespace jsipc {
+
+rpc protocol PContextWrapper
+{
+    manager PIFrameEmbedding or PTestShell;
+    manages PObjectWrapper;
+parent:
+    async __delete__();
+    async PObjectWrapper(bool makeGlobal);
+};
+
+}}
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/PObjectWrapper.ipdl
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol "PContextWrapper.ipdl";
+
+include "mozilla/jsipc/CPOWTypes.h";
+
+using mozilla::jsipc::void_t;
+using JSType;
+using JSBool;
+
+namespace mozilla {
+namespace jsipc {
+
+union JSVariant {
+    void_t;
+    nullable PObjectWrapper;
+    nsString;
+    int;
+    double;
+    bool; // We'd like to use JSBool here, but JSBool is really JSIntn,
+          // and IPC::ParamTraits mistakes JSIntn for int.
+};
+
+rpc protocol PObjectWrapper
+{
+    manager PContextWrapper;
+
+child:
+    __delete__(); // unroot
+
+    rpc AddProperty(nsString id)
+        returns (JSBool ok);
+
+    rpc GetProperty(nsString id)
+        returns (JSBool ok,
+                 JSVariant vp);
+
+    rpc SetProperty(nsString id,
+                    JSVariant v)
+        returns (JSBool ok,
+                 JSVariant vp);
+
+    rpc DelProperty(nsString id)
+        returns (JSBool ok,
+                 JSVariant vp);
+
+    rpc NewEnumerateInit()
+        returns (JSBool ok,
+                 JSVariant statep,
+                 int idp);
+
+    rpc NewEnumerateNext(JSVariant in_state)
+        returns (JSBool ok,
+                 JSVariant statep,
+                 nsString idp);
+
+    async NewEnumerateDestroy(JSVariant in_state);
+
+    rpc NewResolve(nsString id,
+                   int flags)
+        returns (JSBool ok,
+                 nullable PObjectWrapper obj2);
+
+    rpc Convert(JSType type)
+        returns (JSBool ok,
+                 JSVariant vp);
+
+    rpc Call(PObjectWrapper receiver,
+             JSVariant[] argv)
+        returns (JSBool ok,
+                 JSVariant rval);
+
+    rpc Construct(JSVariant[] argv)
+        returns (JSBool ok,
+                 nullable PObjectWrapper rval);
+
+    rpc HasInstance(JSVariant v)
+        returns (JSBool ok,
+                 JSBool bp);
+};
+
+}}
new file mode 100644
--- /dev/null
+++ b/js/src/ipc/ipdl.mk
@@ -0,0 +1,41 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+IPDLSRCS = \
+  PContextWrapper.ipdl \
+  PObjectWrapper.ipdl \
+  $(NULL)
--- a/js/src/xpconnect/shell/xpcshell.cpp
+++ b/js/src/xpconnect/shell/xpcshell.cpp
@@ -676,16 +676,31 @@ SendCommand(JSContext* cx,
     if (!XRE_SendTestShellCommand(cx, str, argc > 1 ? &argv[1] : nsnull)) {
         JS_ReportError(cx, "Couldn't send command!");
         return JS_FALSE;
     }
 
     return JS_TRUE;
 }
 
+static JSBool
+GetChildGlobalObject(JSContext* cx,
+                     JSObject*,
+                     uintN,
+                     jsval*,
+                     jsval* rval)
+{
+    JSObject* global;
+    if (XRE_GetChildGlobalObject(cx, &global)) {
+        *rval = OBJECT_TO_JSVAL(global);
+        return JS_TRUE;
+    }
+    return JS_FALSE;
+}
+
 #endif // MOZ_IPC
 
 /*
  * JSContext option name to flag map. The option names are in alphabetical
  * order for better reporting.
  */
 static const struct {
     const char  *name;
@@ -793,16 +808,17 @@ static JSFunctionSpec glob_functions[] =
     {"gc",              GC,             0,0,0},
     {"clear",           Clear,          1,0,0},
     {"options",         Options,        0,0,0},
 #ifdef DEBUG
     {"dumpHeap",        DumpHeap,       5,0,0},
 #endif
 #ifdef MOZ_IPC
     {"sendCommand",     SendCommand,    1,0,0},
+    {"getChildGlobalObject", GetChildGlobalObject, 0,0,0},
 #endif
 #ifdef MOZ_SHARK
     {"startShark",      js_StartShark,      0,0,0},
     {"stopShark",       js_StopShark,       0,0,0},
     {"connectShark",    js_ConnectShark,    0,0,0},
     {"disconnectShark", js_DisconnectShark, 0,0,0},
 #endif
 #ifdef MOZ_CALLGRIND
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -88,16 +88,17 @@ RCFLAGS += -i $(topsrcdir)/widget/src/os
 endif
 
 LOCAL_INCLUDES += -I$(topsrcdir)/widget/src/os2
 endif
 
 # dependent libraries
 ifdef MOZ_IPC
 STATIC_LIBS += \
+  jsipc_s \
   domipc_s \
   domplugins_s \
   mozipc_s \
   mozipdlgen_s \
   ipcshell_s \
   gfxipc_s \
   $(NULL)
 
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -202,16 +202,20 @@ MAKEFILES_xpconnect="
   js/src/xpconnect/loader/Makefile
   js/src/xpconnect/tests/Makefile
   js/src/xpconnect/tests/components/Makefile
   js/src/xpconnect/tests/idl/Makefile
   js/src/xpconnect/tools/Makefile
   js/src/xpconnect/tools/idl/Makefile
 "
 
+MAKEFILES_jsipc="
+  js/src/ipc/Makefile
+"
+
 MAKEFILES_jsdebugger="
   js/jsd/Makefile
   js/jsd/idl/Makefile
 "
 
 MAKEFILES_jsctypes="
   js/ctypes/Makefile
 "
@@ -837,16 +841,17 @@ add_makefiles "
   $MAKEFILES_db
   $MAKEFILES_dom
   $MAKEFILES_editor
   $MAKEFILES_xmlparser
   $MAKEFILES_gfx
   $MAKEFILES_htmlparser
   $MAKEFILES_intl
   $MAKEFILES_xpconnect
+  $MAKEFILES_jsipc
   $MAKEFILES_jsdebugger
   $MAKEFILES_jsctypes
   $MAKEFILES_content
   $MAKEFILES_layout
   $MAKEFILES_libimg
   $MAKEFILES_libjar
   $MAKEFILES_libreg
   $MAKEFILES_libpref
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -101,17 +101,17 @@ ifeq ($(OS_ARCH),WINCE)
 tier_platform_dirs += modules/lib7z
 endif
 
 #
 # "gecko" - core components
 #
 
 ifdef MOZ_IPC
-tier_platform_dirs += ipc
+tier_platform_dirs += ipc js/src/ipc
 endif
 
 tier_platform_dirs += \
 		js/src/xpconnect \
 		js/ctypes \
 		intl/chardet \
 		$(NULL)
 
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -86,16 +86,18 @@
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "ScopedXREEmbed.h"
 
 #include "mozilla/plugins/PluginThreadChild.h"
 #include "mozilla/dom/ContentProcessThread.h"
 #include "mozilla/dom/ContentProcessParent.h"
 #include "mozilla/dom/ContentProcessChild.h"
 
+#include "mozilla/jsipc/ContextWrapperParent.h"
+
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/Monitor.h"
 
 #ifdef MOZ_IPDL_TESTS
 #include "mozilla/_ipdltest/IPDLUnitTests.h"
 #include "mozilla/_ipdltest/IPDLUnitTestThreadChild.h"
 
@@ -105,16 +107,20 @@ using mozilla::_ipdltest::IPDLUnitTestTh
 using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::ipc::BrowserProcessSubThread;
 using mozilla::ipc::ScopedXREEmbed;
 
 using mozilla::plugins::PluginThreadChild;
 using mozilla::dom::ContentProcessThread;
 using mozilla::dom::ContentProcessParent;
 using mozilla::dom::ContentProcessChild;
+
+using mozilla::jsipc::PContextWrapperParent;
+using mozilla::jsipc::ContextWrapperParent;
+
 using mozilla::ipc::TestShellParent;
 using mozilla::ipc::TestShellCommandParent;
 using mozilla::ipc::XPCShellEnvironment;
 
 using mozilla::Monitor;
 using mozilla::MonitorAutoEnter;
 
 using mozilla::startup::sChildProcessType;
@@ -509,48 +515,60 @@ XRE_ShutdownChildProcess()
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   NS_ABORT_IF_FALSE(!!ioLoop, "Bad shutdown order");
 
   ioLoop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
 }
 
 namespace {
 TestShellParent* gTestShellParent = nsnull;
+TestShellParent* GetOrCreateTestShellParent()
+{
+    if (!gTestShellParent) {
+        ContentProcessParent* parent = ContentProcessParent::GetSingleton();
+        NS_ENSURE_TRUE(parent, nsnull);
+        gTestShellParent = parent->CreateTestShell();
+        NS_ENSURE_TRUE(gTestShellParent, nsnull);
+    }
+    return gTestShellParent;
+}
 }
 
 bool
 XRE_SendTestShellCommand(JSContext* aCx,
                          JSString* aCommand,
                          void* aCallback)
 {
-    if (!gTestShellParent) {
-        ContentProcessParent* parent = ContentProcessParent::GetSingleton();
-        NS_ENSURE_TRUE(parent, false);
-
-        gTestShellParent = parent->CreateTestShell();
-        NS_ENSURE_TRUE(gTestShellParent, false);
-    }
+    TestShellParent* tsp = GetOrCreateTestShellParent();
+    NS_ENSURE_TRUE(tsp, false);
 
     nsDependentString command((PRUnichar*)JS_GetStringChars(aCommand),
                               JS_GetStringLength(aCommand));
     if (!aCallback) {
-        return gTestShellParent->SendExecuteCommand(command);
+        return tsp->SendExecuteCommand(command);
     }
 
     TestShellCommandParent* callback = static_cast<TestShellCommandParent*>(
-        gTestShellParent->SendPTestShellCommandConstructor(command));
+        tsp->SendPTestShellCommandConstructor(command));
     NS_ENSURE_TRUE(callback, false);
 
     jsval callbackVal = *reinterpret_cast<jsval*>(aCallback);
     NS_ENSURE_TRUE(callback->SetCallback(aCx, callbackVal), false);
 
     return true;
 }
 
 bool
+XRE_GetChildGlobalObject(JSContext* aCx, JSObject** aGlobalP)
+{
+    TestShellParent* tsp = GetOrCreateTestShellParent();
+    return tsp && tsp->GetGlobalJSObject(aCx, aGlobalP);
+}
+
+bool
 XRE_ShutdownTestShell()
 {
   if (!gTestShellParent)
     return true;
   return ContentProcessParent::GetSingleton()->DestroyTestShell(gTestShellParent);
 }
 
 #ifdef MOZ_X11
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -498,14 +498,20 @@ XRE_API(MessageLoop*,
 
 struct JSContext;
 struct JSString;
 
 XRE_API(bool,
         XRE_SendTestShellCommand, (JSContext* aCx,
                                    JSString* aCommand,
                                    void* aCallback))
+struct JSObject;
+
+XRE_API(bool,
+        XRE_GetChildGlobalObject, (JSContext* aCx,
+                                   JSObject** globalp))
+
 XRE_API(bool,
         XRE_ShutdownTestShell, ())
 
 XRE_API(void,
         XRE_InstallX11ErrorHandler, ())
 #endif // _nsXULAppAPI_h__