Bug 1162838 - Allow sending initial process data to content processes (r=smaug)
authorBill McCloskey <billm@mozilla.com>
Thu, 30 Apr 2015 18:20:51 -0700
changeset 243448 cd4e131df42891772d29d5141ce2c39a12170fe0
parent 243447 f1eec08c595b523c14b967f98c08dd5ebed58efd
child 243449 e6565b1773b8216aa8ebdf3cbd4573d62a0dc0a4
push id28738
push usercbook@mozilla.com
push dateTue, 12 May 2015 14:11:31 +0000
treeherdermozilla-central@bedce1b405a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1162838
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1162838 - Allow sending initial process data to content processes (r=smaug)
dom/base/ProcessGlobal.cpp
dom/base/ProcessGlobal.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsFrameMessageManager.cpp
dom/base/nsFrameMessageManager.h
dom/base/nsIMessageManager.idl
dom/base/test/browser_messagemanager_loadprocessscript.js
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ProcessGlobal.cpp
@@ -98,8 +98,14 @@ ProcessGlobal::Init()
 }
 
 void
 ProcessGlobal::LoadScript(const nsAString& aURL)
 {
   Init();
   LoadScriptInternal(aURL, false);
 }
+
+void
+ProcessGlobal::SetInitialProcessData(JS::HandleValue aInitialData)
+{
+  mMessageManager->SetInitialProcessData(aInitialData);
+}
--- a/dom/base/ProcessGlobal.h
+++ b/dom/base/ProcessGlobal.h
@@ -40,16 +40,17 @@ public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ProcessGlobal, nsIContentProcessMessageManager)
 
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
   NS_FORWARD_SAFE_NSISYNCMESSAGESENDER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGEMANAGERGLOBAL(mMessageManager)
+  NS_FORWARD_SAFE_NSICONTENTPROCESSMESSAGEMANAGER(mMessageManager)
 
   virtual void LoadScript(const nsAString& aURL);
 
   virtual JSObject* GetGlobalJSObject() override
   {
     if (!mGlobal) {
       return nullptr;
     }
@@ -58,16 +59,18 @@ public:
   }
   virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
   {
     MOZ_CRASH("ProcessGlobal doesn't use DOM bindings!");
   }
 
+  void SetInitialProcessData(JS::HandleValue aInitialData);
+
 protected:
   virtual ~ProcessGlobal();
 
 private:
   bool mInitialized;
   nsRefPtr<nsFrameMessageManager> mMessageManager;
 };
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -679,16 +679,17 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
     DOM_CLASSINFO_MAP_ENTRY(nsISyncMessageSender)
     DOM_CLASSINFO_MAP_ENTRY(nsIContentProcessMessageManager)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageBroadcaster, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader)
+    DOM_CLASSINFO_MAP_ENTRY(nsIGlobalProcessScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageBroadcaster)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageSender, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIProcessChecker)
     DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader)
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -57,16 +57,71 @@
 #  undef SendMessage
 # endif
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
+nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
+                                             nsFrameMessageManager* aParentManager,
+                                             /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
+ : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
+   mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
+   mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
+   mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
+   mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
+  mHandlingMessage(false),
+  mClosed(false),
+  mDisconnected(false),
+  mCallback(aCallback),
+  mParentManager(aParentManager)
+{
+  NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!");
+  NS_ASSERTION(!mIsBroadcaster || !mCallback,
+               "Broadcasters cannot have callbacks!");
+  if (mIsProcessManager && (!mChrome || IsBroadcaster())) {
+    mozilla::HoldJSObjects(this);
+  }
+  // This is a bit hackish. When parent manager is global, we want
+  // to attach the message manager to it immediately.
+  // Is it just the frame message manager which waits until the
+  // content process is running.
+  if (mParentManager && (mCallback || IsBroadcaster())) {
+    mParentManager->AddChildManager(this);
+  }
+  if (mOwnsCallback) {
+    mOwnedCallback = aCallback;
+  }
+}
+
+nsFrameMessageManager::~nsFrameMessageManager()
+{
+  if (mIsProcessManager && (!mChrome || IsBroadcaster())) {
+    mozilla::DropJSObjects(this);
+  }
+  for (int32_t i = mChildManagers.Count(); i > 0; --i) {
+    static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])->
+      Disconnect(false);
+  }
+  if (mIsProcessManager) {
+    if (this == sParentProcessManager) {
+      sParentProcessManager = nullptr;
+    }
+    if (this == sChildProcessManager) {
+      sChildProcessManager = nullptr;
+      delete mozilla::dom::SameProcessMessageQueue::Get();
+    }
+    if (this == sSameProcessParentManager) {
+      sSameProcessParentManager = nullptr;
+    }
+  }
+}
+
 static PLDHashOperator
 CycleCollectorTraverseListeners(const nsAString& aKey,
                                 nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
                                 void* aCb)
 {
   nsCycleCollectionTraversalCallback* cb =
     static_cast<nsCycleCollectionTraversalCallback*> (aCb);
   uint32_t count = aListeners->Length();
@@ -78,25 +133,31 @@ CycleCollectorTraverseListeners(const ns
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
   tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners,
                                 static_cast<void*>(&cb));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mInitialProcessData)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
   tmp->mListeners.Clear();
   for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
     static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
       Disconnect(false);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
+  tmp->mInitialProcessData.setNull();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager)
 
   /* nsFrameMessageManager implements nsIMessageSender and nsIMessageBroadcaster,
    * both of which descend from nsIMessageListenerManager. QI'ing to
@@ -124,16 +185,20 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   /* Frame message managers (non-process message managers) support nsIFrameScriptLoader. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFrameScriptLoader,
                                      mChrome && !mIsProcessManager)
 
   /* Process message managers (process message managers) support nsIProcessScriptLoader. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessScriptLoader,
                                      mChrome && mIsProcessManager)
 
+  /* Global process message managers (process message managers) support nsIGlobalProcessScriptLoader. */
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIGlobalProcessScriptLoader,
+                                     mChrome && mIsProcessManager && mIsBroadcaster)
+
   /* Message senders in the chrome process support nsIProcessChecker. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessChecker,
                                      mChrome && !mIsBroadcaster)
 
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageBroadcaster,
                                                    mChrome && mIsBroadcaster)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender,
                                                    mChrome && !mIsBroadcaster)
@@ -1327,16 +1392,53 @@ nsFrameMessageManager::Disconnect(bool a
   }
   mDisconnected = true;
   mParentManager = nullptr;
   if (!mHandlingMessage) {
     mListeners.Clear();
   }
 }
 
+void
+nsFrameMessageManager::SetInitialProcessData(JS::HandleValue aInitialData)
+{
+  MOZ_ASSERT(!mChrome);
+  MOZ_ASSERT(mIsProcessManager);
+  mInitialProcessData = aInitialData;
+}
+
+NS_IMETHODIMP
+nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleValue aResult)
+{
+  MOZ_ASSERT(mIsProcessManager);
+  MOZ_ASSERT_IF(mChrome, IsBroadcaster());
+
+  JS::RootedValue init(aCx, mInitialProcessData);
+  if (mChrome && init.isUndefined()) {
+    // We create the initial object in the junk scope. If we created it in a
+    // normal compartment, that compartment would leak until shutdown.
+    JS::RootedObject global(aCx, xpc::PrivilegedJunkScope());
+    JSAutoCompartment ac(aCx, global);
+
+    JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
+    if (!obj) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    mInitialProcessData.setObject(*obj);
+    init.setObject(*obj);
+  }
+
+  if (!JS_WrapValue(aCx, &init)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  aResult.set(init);
+  return NS_OK;
+}
+
 namespace {
 
 struct MessageManagerReferentCount
 {
   MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
   size_t mStrong;
   size_t mWeakAlive;
   size_t mWeakDead;
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -157,85 +157,43 @@ public:
 
 private:
   JS::Rooted<JSObject*> mObj;
 };
 
 class nsFrameMessageManager final : public nsIContentFrameMessageManager,
                                     public nsIMessageBroadcaster,
                                     public nsIFrameScriptLoader,
-                                    public nsIProcessScriptLoader,
+                                    public nsIGlobalProcessScriptLoader,
                                     public nsIProcessChecker
 {
   friend class mozilla::dom::MessageManagerReporter;
   typedef mozilla::dom::StructuredCloneData StructuredCloneData;
   typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer;
 public:
   nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                         nsFrameMessageManager* aParentManager,
-                        /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
-  : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
-    mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
-    mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
-    mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
-    mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
-    mHandlingMessage(false),
-    mClosed(false),
-    mDisconnected(false),
-    mCallback(aCallback),
-    mParentManager(aParentManager)
-  {
-    NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!");
-    NS_ASSERTION(!mIsBroadcaster || !mCallback,
-                 "Broadcasters cannot have callbacks!");
-    // This is a bit hackish. When parent manager is global, we want
-    // to attach the message manager to it immediately.
-    // Is it just the frame message manager which waits until the
-    // content process is running.
-    if (mParentManager && (mCallback || IsBroadcaster())) {
-      mParentManager->AddChildManager(this);
-    }
-    if (mOwnsCallback) {
-      mOwnedCallback = aCallback;
-    }
-  }
+                        /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags);
 
 private:
-  ~nsFrameMessageManager()
-  {
-    for (int32_t i = mChildManagers.Count(); i > 0; --i) {
-      static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])->
-        Disconnect(false);
-    }
-    if (mIsProcessManager) {
-      if (this == sParentProcessManager) {
-        sParentProcessManager = nullptr;
-      }
-      if (this == sChildProcessManager) {
-        sChildProcessManager = nullptr;
-        delete mozilla::dom::SameProcessMessageQueue::Get();
-      }
-      if (this == sSameProcessParentManager) {
-        sSameProcessParentManager = nullptr;
-      }
-    }
-  }
+  ~nsFrameMessageManager();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameMessageManager,
-                                           nsIContentFrameMessageManager)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFrameMessageManager,
+                                                         nsIContentFrameMessageManager)
   NS_DECL_NSIMESSAGELISTENERMANAGER
   NS_DECL_NSIMESSAGESENDER
   NS_DECL_NSIMESSAGEBROADCASTER
   NS_DECL_NSISYNCMESSAGESENDER
   NS_DECL_NSIMESSAGEMANAGERGLOBAL
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSIFRAMESCRIPTLOADER
   NS_DECL_NSIPROCESSSCRIPTLOADER
+  NS_DECL_NSIGLOBALPROCESSSCRIPTLOADER
   NS_DECL_NSIPROCESSCHECKER
 
   static nsFrameMessageManager*
   NewProcessMessageManager(bool aIsRemote);
 
   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
                           const nsAString& aMessage,
                           bool aIsSync, const StructuredCloneData* aCloneData,
@@ -286,16 +244,19 @@ public:
   static nsFrameMessageManager* GetChildProcessManager()
   {
     return sChildProcessManager;
   }
   static void SetChildProcessManager(nsFrameMessageManager* aManager)
   {
     sChildProcessManager = aManager;
   }
+
+  void SetInitialProcessData(JS::HandleValue aInitialData);
+
 private:
   nsresult SendMessage(const nsAString& aMessageName,
                        JS::Handle<JS::Value> aJSON,
                        JS::Handle<JS::Value> aObjects,
                        nsIPrincipal* aPrincipal,
                        JSContext* aCx,
                        uint8_t aArgc,
                        JS::MutableHandle<JS::Value> aRetval,
@@ -328,16 +289,17 @@ protected:
   bool mHandlingMessage;
   bool mClosed;    // true if we can no longer send messages
   bool mDisconnected;
   mozilla::dom::ipc::MessageManagerCallback* mCallback;
   nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
   nsFrameMessageManager* mParentManager;
   nsTArray<nsString> mPendingScripts;
   nsTArray<bool> mPendingScriptsGlobalStates;
+  JS::Heap<JS::Value> mInitialProcessData;
 
   void LoadPendingScripts(nsFrameMessageManager* aManager,
                           nsFrameMessageManager* aChildMM);
 public:
   static nsFrameMessageManager* sParentProcessManager;
   static nsFrameMessageManager* sSameProcessParentManager;
   static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
 private:
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -386,22 +386,28 @@ interface nsIContentFrameMessageManager 
 
 [uuid(9c6bd4d7-88d2-46d6-8606-f2d57d46f051)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
   [notxpcom] void cacheFrameLoader(in nsIFrameLoader aFrameLoader);
 };
 
-[scriptable, builtinclass, uuid(d0c799a2-d5ff-4a75-acbb-b8c8347944a6)]
+[scriptable, builtinclass, uuid(6d12e467-2446-46db-9965-e4e93cb87ca5)]
 interface nsIContentProcessMessageManager : nsIMessageManagerGlobal
 {
+  /**
+   * Read out a copy of the object that was initialized in the parent
+   * process via nsIProcessScriptLoader.initialProcessData.
+   */
+  [implicit_jscontext]
+  readonly attribute jsval initialProcessData;
 };
 
-[scriptable, builtinclass, uuid(6fb78110-45ae-11e3-8f96-0800200c9a66)]
+[scriptable, builtinclass, uuid(bf61446b-ba24-4b1d-88c7-4f94724b9ce1)]
 interface nsIFrameScriptLoader : nsISupports
 {
   /**
    * Load a script in the (remote) frame. aURL must be the absolute URL.
    * data: URLs are also supported. For example data:,dump("foo\n");
    * If aAllowDelayedLoad is true, script will be loaded when the
    * remote frame becomes available. Otherwise the script will be loaded
    * only if the frame is already available.
@@ -443,16 +449,36 @@ interface nsIProcessScriptLoader : nsISu
   /**
    * Returns all delayed scripts that will be loaded once a (remote)
    * frame becomes available. The return value is a list of URLs.
    */
   [implicit_jscontext]
   jsval getDelayedProcessScripts();
 };
 
+[scriptable, builtinclass, uuid(5b390753-abb3-49b0-ae3b-b803dab58144)]
+interface nsIGlobalProcessScriptLoader : nsIProcessScriptLoader
+{
+  /**
+   * Allows the parent process to set the initial process data for
+   * new, not-yet-created child processes. This attribute should only
+   * be used by the global parent process message manager. When a new
+   * process is created, it gets a copy of this data (via structured
+   * cloning). It can access the data via the initialProcessData
+   * attribute of its childprocessmessagemanager.
+   *
+   * This value will always be a JS object. Different users are
+   * expected to set properties on this object. The property name
+   * should be unique enough that other Gecko consumers won't
+   * accidentally choose it.
+   */
+  [implicit_jscontext]
+  readonly attribute jsval initialProcessData;
+};
+
 [scriptable, builtinclass, uuid(637e8538-4f8f-4a3d-8510-e74386233e19)]
 interface nsIProcessChecker : nsISupports
 {
   bool killChild();
 
   /**
    * Return true if the "remote" process has |aPermission|.  This is
    * intended to be used by JS implementations of cross-process DOM
--- a/dom/base/test/browser_messagemanager_loadprocessscript.js
+++ b/dom/base/test/browser_messagemanager_loadprocessscript.js
@@ -15,16 +15,27 @@ function processScript() {
   addMessageListener("ProcessTest:Reply", function listener(msg) {
     removeMessageListener("ProcessTest:Reply", listener);
     sendAsyncMessage("ProcessTest:Finished");
   });
   sendSyncMessage("ProcessTest:Loaded");
 }
 let processScriptURL = "data:,(" + processScript.toString() + ")()";
 
+function initTestScript() {
+  let init = initialProcessData;
+  if (init.test123 != "hello") {
+    dump("Initial data incorrect\n");
+    return;
+  }
+
+  sendAsyncMessage("ProcessTest:InitGood", init.test456.get("hi"));
+}
+let initTestScriptURL = "data:,(" + initTestScript.toString() + ")()";
+
 let checkProcess = Task.async(function*(mm) {
   let { target } = yield promiseMessage(mm, "ProcessTest:Loaded");
   target.sendAsyncMessage("ProcessTest:Reply");
   yield promiseMessage(target, "ProcessTest:Finished");
   ok(true, "Saw process finished");
 });
 
 function promiseMessage(messageManager, message) {
@@ -55,21 +66,28 @@ add_task(function*() {
     return;
 
   is(ppmm.childCount, 2, "Should be two processes at this point");
 
   // Load something in the main process
   gBrowser.selectedBrowser.loadURI("about:robots");
   yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
+  let init = ppmm.initialProcessData;
+  init.test123 = "hello";
+  init.test456 = new Map();
+  init.test456.set("hi", "bye");
+
   // With no remote frames left we should be down to one process.
   // However, stuff like remote thumbnails can cause a content
   // process to exist nonetheless. This should be rare, though,
   // so the test is useful most of the time.
   if (ppmm.childCount == 1) {
+    let mainMM = ppmm.getChildAt(0);
+
     let check = checkProcess(ppmm);
     ppmm.loadProcessScript(processScriptURL, true);
 
     // The main process should respond
     yield check;
 
     check = checkProcess(ppmm);
     // Reset the default browser to start a new child process
@@ -78,12 +96,19 @@ add_task(function*() {
     yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
     is(ppmm.childCount, 2, "Should be back to two processes at this point");
 
     // The new process should have responded
     yield check;
 
     ppmm.removeDelayedProcessScript(processScriptURL);
+
+    let childMM;
+    childMM = ppmm.getChildAt(0) == mainMM ? ppmm.getChildAt(1) : ppmm.getChildAt(0);
+
+    childMM.loadProcessScript(initTestScriptURL, false);
+    let msg = yield promiseMessage(childMM, "ProcessTest:InitGood");
+    is(msg.data, "bye", "initial process data was correct");
   } else {
     info("Unable to finish test entirely");
   }
 });
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -807,20 +807,21 @@ ContentChild::InitXPCOM()
     mConsoleListener = new ConsoleListener(this);
     if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
         NS_WARNING("Couldn't register console listener for child process");
 
     bool isOffline, isLangRTL;
     bool isConnected;
     ClipboardCapabilities clipboardCaps;
     DomainPolicyClone domainPolicy;
+    OwningSerializedStructuredCloneBuffer initialData;
 
     SendGetXPCOMProcessAttributes(&isOffline, &isConnected,
                                   &isLangRTL, &mAvailableDictionaries,
-                                  &clipboardCaps, &domainPolicy);
+                                  &clipboardCaps, &domainPolicy, &initialData);
     RecvSetOffline(isOffline);
     RecvSetConnectivity(isConnected);
     RecvBidiKeyboardNotify(isLangRTL);
 
     // Create the CPOW manager as soon as possible.
     SendPJavaScriptConstructor();
 
     if (domainPolicy.active()) {
@@ -833,16 +834,30 @@ ContentChild::InitXPCOM()
         mPolicy->ApplyClone(&domainPolicy);
     }
 
     nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
     if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
         clipboardProxy->SetCapabilities(clipboardCaps);
     }
 
+    {
+        AutoJSAPI jsapi;
+        if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+            MOZ_CRASH();
+        }
+        JS::RootedValue data(jsapi.cx());
+        if (!JS_ReadStructuredClone(jsapi.cx(), initialData.data, initialData.dataLength,
+                                    JS_STRUCTURED_CLONE_VERSION, &data, nullptr, nullptr)) {
+            MOZ_CRASH();
+        }
+        ProcessGlobal* global = ProcessGlobal::Get();
+        global->SetInitialProcessData(data);
+    }
+
     DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
     NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 
     // This object is held alive by the observer service.
     nsRefPtr<SystemMessageHandledObserver> sysMsgObserver =
         new SystemMessageHandledObserver();
     sysMsgObserver->Init();
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2939,20 +2939,21 @@ ContentParent::RecvAddNewProcess(const u
     }
 
     // Update offline settings.
     bool isOffline, isLangRTL;
     bool isConnected;
     InfallibleTArray<nsString> unusedDictionaries;
     ClipboardCapabilities clipboardCaps;
     DomainPolicyClone domainPolicy;
+    OwningSerializedStructuredCloneBuffer initialData;
 
     RecvGetXPCOMProcessAttributes(&isOffline, &isConnected,
                                   &isLangRTL, &unusedDictionaries,
-                                  &clipboardCaps, &domainPolicy);
+                                  &clipboardCaps, &domainPolicy, &initialData);
     mozilla::unused << content->SendSetOffline(isOffline);
     mozilla::unused << content->SendSetConnectivity(isConnected);
     MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() &&
                !clipboardCaps.supportsFindClipboard(),
                "Unexpected values");
 
     PreallocatedProcessManager::PublishSpareProcess(content);
     return true;
@@ -3269,17 +3270,18 @@ ContentParent::RecvGetProcessAttributes(
 }
 
 bool
 ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
                                              bool* aIsConnected,
                                              bool* aIsLangRTL,
                                              InfallibleTArray<nsString>* dictionaries,
                                              ClipboardCapabilities* clipboardCaps,
-                                             DomainPolicyClone* domainPolicy)
+                                             DomainPolicyClone* domainPolicy,
+                                             OwningSerializedStructuredCloneBuffer* initialData)
 {
     nsCOMPtr<nsIIOService> io(do_GetIOService());
     MOZ_ASSERT(io, "No IO service?");
     DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
 
     rv = io->GetConnectivity(aIsConnected);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
@@ -3305,16 +3307,35 @@ ContentParent::RecvGetXPCOMProcessAttrib
     rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     // Let's copy the domain policy from the parent to the child (if it's active).
     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
     NS_ENSURE_TRUE(ssm, false);
     ssm->CloneDomainPolicy(domainPolicy);
 
+    if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) {
+        AutoJSAPI jsapi;
+        if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+            return false;
+        }
+        JS::RootedValue init(jsapi.cx());
+        nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init);
+        if (NS_FAILED(result)) {
+            return false;
+        }
+
+        JSAutoStructuredCloneBuffer buffer;
+        if (!buffer.write(jsapi.cx(), init)) {
+            return false;
+        }
+
+        buffer.steal(&initialData->data, &initialData->dataLength);
+    }
+
     return true;
 }
 
 mozilla::jsipc::PJavaScriptParent *
 ContentParent::AllocPJavaScriptParent()
 {
     MOZ_ASSERT(!ManagedPJavaScriptParent().Length());
     return nsIContentParent::AllocPJavaScriptParent();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -549,17 +549,18 @@ private:
     virtual bool RecvGetProcessAttributes(ContentParentId* aCpId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) override;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
                                                bool* aIsConnected,
                                                bool* aIsLangRTL,
                                                InfallibleTArray<nsString>* dictionaries,
                                                ClipboardCapabilities* clipboardCaps,
-                                               DomainPolicyClone* domainPolicy) override;
+                                               DomainPolicyClone* domainPolicy,
+                                               OwningSerializedStructuredCloneBuffer* initialData) override;
 
     virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
 
     virtual bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) override;
     virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
                                                 const IPCTabContext& aContext,
                                                 const uint32_t& aChromeFlags,
                                                 const ContentParentId& aCpId,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -675,17 +675,18 @@ parent:
      *
      * Keep the return values in sync with PBrowser()!
      */
     sync GetProcessAttributes()
         returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
     sync GetXPCOMProcessAttributes()
         returns (bool isOffline, bool isConnected, bool isLangRTL, nsString[] dictionaries,
                  ClipboardCapabilities clipboardCaps,
-                 DomainPolicyClone domainPolicy);
+                 DomainPolicyClone domainPolicy,
+                 OwningSerializedStructuredCloneBuffer initialData);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId);
 
     async CreateGMPService();