Bug 998863: Asynchronous Plugin Initialization, Part 9: PluginModuleParent changes; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Mon, 29 Dec 2014 16:13:54 -0700
changeset 247412 8a4c2bf5ec09977e94cc055267c0bc6280914966
parent 247411 eb6ac47db85693228e942143cfbe5a077e8d6127
child 247413 ce5bf5f8493215a05e63c596f95ca78c3fd157cd
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs998863
milestone37.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 998863: Asynchronous Plugin Initialization, Part 9: PluginModuleParent changes; r=jimm
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/PContent.ipdl
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
dom/plugins/ipc/PluginProcessParent.cpp
modules/libpref/init/all.js
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -946,17 +946,17 @@ ContentChild::DeallocPCycleCollectWithLo
     // this point, so we shouldn't touch the actor in any case.
     return true;
 }
 
 mozilla::plugins::PPluginModuleParent*
 ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
-    return plugins::PluginModuleContentParent::Create(aTransport, aOtherProcess);
+    return plugins::PluginModuleContentParent::Initialize(aTransport, aOtherProcess);
 }
 
 PContentBridgeChild*
 ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
     return ContentBridgeChild::Create(aTransport, aOtherProcess);
 }
@@ -2469,16 +2469,31 @@ ContentChild::RecvGetProfile(nsCString* 
         *aProfile = nsCString(profile, strlen(profile));
         free(profile);
     } else {
         *aProfile = EmptyCString();
     }
     return true;
 }
 
+bool
+ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult)
+{
+    plugins::PluginModuleContentParent::OnLoadPluginResult(aPluginId, aResult);
+    return true;
+}
+
+bool
+ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId,
+                                    const base::ProcessId& aProcessId)
+{
+    plugins::PluginModuleContentParent::AssociatePluginId(aPluginId, aProcessId);
+    return true;
+}
+
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
 {
     if (!aTabChild ||
         this == aTabChild->Manager()) {
         return PBrowserOrId(aTabChild);
     }
     else {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -357,16 +357,21 @@ public:
     void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
     void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
     virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver,
                                         const nsCString& aTopic,
                                         const nsString& aData) MOZ_OVERRIDE;
 
     virtual bool RecvOnAppThemeChanged() MOZ_OVERRIDE;
 
+    virtual bool RecvAssociatePluginId(const uint32_t& aPluginId,
+                                       const base::ProcessId& aProcessId) MOZ_OVERRIDE;
+    virtual bool RecvLoadPluginResult(const uint32_t& aPluginId,
+                                      const bool& aResult) MOZ_OVERRIDE;
+
     virtual bool RecvStartProfiler(const uint32_t& aEntries,
                                    const double& aInterval,
                                    const nsTArray<nsCString>& aFeatures,
                                    const nsTArray<nsCString>& aThreadNameFilters) MOZ_OVERRIDE;
     virtual bool RecvStopProfiler() MOZ_OVERRIDE;
     virtual bool RecvGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
 
 #ifdef ANDROID
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -54,16 +54,17 @@ include "mozilla/dom/PContentBridgeParen
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
 using struct ResourceMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ChildPrivileges from "base/process_util.h";
+using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
 using mozilla::dom::asmjscache::WriteParams from "mozilla/dom/asmjscache/AsmJSCache.h";
 using mozilla::dom::AudioChannel from "mozilla/dom/AudioChannelBinding.h";
 using mozilla::dom::AudioChannelState from "AudioChannelCommon.h";
@@ -514,16 +515,29 @@ child:
     NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str);
 
     /**
      * Notify windows in the child to apply a new app style.
      */
     OnAppThemeChanged();
 
     /**
+     * Called during plugin initialization to map a plugin id to a child process
+     * id.
+     */
+    async AssociatePluginId(uint32_t aPluginId, ProcessId aProcessId);
+
+    /**
+     * This call is used by async plugin initialization to notify the
+     * PluginModuleContentParent that the PluginModuleChromeParent's async
+     * init has completed.
+     */
+    async LoadPluginResult(uint32_t aPluginId, bool aResult);
+
+    /**
      * Control the Gecko Profiler in the child process.
      */
     async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                         nsCString[] aThreadNameFilters);
     async StopProfiler();
     prio(high) sync GetProfile()
       returns (nsCString aProfile);
 
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -162,21 +162,17 @@ PluginInstanceParent::Destroy()
     return retval;
 }
 
 PBrowserStreamParent*
 PluginInstanceParent::AllocPBrowserStreamParent(const nsCString& url,
                                                 const uint32_t& length,
                                                 const uint32_t& lastmodified,
                                                 PStreamNotifyParent* notifyData,
-                                                const nsCString& headers,
-                                                const nsCString& mimeType,
-                                                const bool& seekable,
-                                                NPError* rv,
-                                                uint16_t *stype)
+                                                const nsCString& headers)
 {
     NS_RUNTIMEABORT("Not reachable");
     return nullptr;
 }
 
 bool
 PluginInstanceParent::DeallocPBrowserStreamParent(PBrowserStreamParent* stream)
 {
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -69,21 +69,18 @@ public:
 
     virtual bool
     DeallocPPluginScriptableObjectParent(PPluginScriptableObjectParent* aObject) MOZ_OVERRIDE;
     virtual PBrowserStreamParent*
     AllocPBrowserStreamParent(const nsCString& url,
                               const uint32_t& length,
                               const uint32_t& lastmodified,
                               PStreamNotifyParent* notifyData,
-                              const nsCString& headers,
-                              const nsCString& mimeType,
-                              const bool& seekable,
-                              NPError* rv,
-                              uint16_t *stype) MOZ_OVERRIDE;
+                              const nsCString& headers)
+                              MOZ_OVERRIDE;
     virtual bool
     DeallocPBrowserStreamParent(PBrowserStreamParent* stream) MOZ_OVERRIDE;
 
     virtual PPluginStreamParent*
     AllocPPluginStreamParent(const nsCString& mimeType,
                              const nsCString& target,
                              NPError* result) MOZ_OVERRIDE;
     virtual bool
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -12,16 +12,17 @@
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PCrashReporterParent.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
@@ -65,16 +66,17 @@ using namespace mozilla::plugins::parent
 #include "mozilla/dom/CrashReporterParent.h"
 
 using namespace CrashReporter;
 #endif
 
 static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
 static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
 static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
+static const char kAsyncInitPref[] = "dom.ipc.plugins.asyncInit";
 #ifdef XP_WIN
 static const char kHangUITimeoutPref[] = "dom.ipc.plugins.hangUITimeoutSecs";
 static const char kHangUIMinDisplayPref[] = "dom.ipc.plugins.hangUIMinDisplaySecs";
 #define CHILD_TIMEOUT_PREF kHangUITimeoutPref
 #else
 #define CHILD_TIMEOUT_PREF kChildTimeoutPref
 #endif
 
@@ -90,134 +92,415 @@ bool
 mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent)
 {
     nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
     nsRefPtr<nsNPAPIPlugin> plugin;
     nsresult rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
     if (NS_FAILED(rv)) {
         return false;
     }
-    PluginModuleParent* chromeParent = static_cast<PluginModuleParent*>(plugin->GetLibrary());
+    PluginModuleChromeParent* chromeParent = static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
+    chromeParent->SetContentParent(aContentParent);
+    if (chromeParent->IsStartingAsync()) {
+        // We'll handle the bridging asynchronously
+        return true;
+    }
     return PPluginModule::Bridge(aContentParent, chromeParent);
 }
 
-PluginModuleContentParent* PluginModuleContentParent::sSavedModuleParent;
+/**
+ * Objects of this class remain linked until either an error occurs in the
+ * plugin initialization sequence, or until
+ * PluginModuleContentParent::OnLoadPluginResult has completed executing.
+ */
+class PluginModuleMapping : public PRCList
+{
+public:
+    explicit PluginModuleMapping(uint32_t aPluginId)
+        : mPluginId(aPluginId)
+        , mProcessIdValid(false)
+        , mModule(nullptr)
+        , mChannelOpened(false)
+    {
+        MOZ_COUNT_CTOR(PluginModuleMapping);
+        PR_INIT_CLIST(this);
+        PR_APPEND_LINK(this, &sModuleListHead);
+    }
+
+    ~PluginModuleMapping()
+    {
+        PR_REMOVE_LINK(this);
+        MOZ_COUNT_DTOR(PluginModuleMapping);
+    }
+
+    bool
+    IsChannelOpened() const
+    {
+        return mChannelOpened;
+    }
+
+    void
+    SetChannelOpened()
+    {
+        mChannelOpened = true;
+    }
+
+    PluginModuleContentParent*
+    GetModule()
+    {
+        if (!mModule) {
+            mModule = new PluginModuleContentParent();
+        }
+        return mModule;
+    }
+
+    static PluginModuleMapping*
+    AssociateWithProcessId(uint32_t aPluginId, base::ProcessId aProcessId)
+    {
+        PluginModuleMapping* mapping =
+            static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
+        while (mapping != &sModuleListHead) {
+            if (mapping->mPluginId == aPluginId) {
+                mapping->AssociateWithProcessId(aProcessId);
+                return mapping;
+            }
+            mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
+        }
+        return nullptr;
+    }
+
+    static PluginModuleMapping*
+    Resolve(base::ProcessId aProcessId)
+    {
+        PluginModuleMapping* mapping = nullptr;
+
+        if (sIsLoadModuleOnStack) {
+            // Special case: If loading synchronously, we just need to access
+            // the tail entry of the list.
+            mapping =
+                static_cast<PluginModuleMapping*>(PR_LIST_TAIL(&sModuleListHead));
+            MOZ_ASSERT(mapping);
+            return mapping;
+        }
+
+        mapping =
+            static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
+        while (mapping != &sModuleListHead) {
+            if (mapping->mProcessIdValid && mapping->mProcessId == aProcessId) {
+                return mapping;
+            }
+            mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
+        }
+        return nullptr;
+    }
+
+    static PluginModuleMapping*
+    FindModuleByPluginId(uint32_t aPluginId)
+    {
+        PluginModuleMapping* mapping =
+            static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
+        while (mapping != &sModuleListHead) {
+            if (mapping->mPluginId == aPluginId) {
+                return mapping;
+            }
+            mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
+        }
+        return nullptr;
+    }
+
+    static bool
+    IsLoadModuleOnStack()
+    {
+        return sIsLoadModuleOnStack;
+    }
+
+    class MOZ_STACK_CLASS NotifyLoadingModule
+    {
+    public:
+        explicit NotifyLoadingModule(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+        {
+            MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+            PluginModuleMapping::sIsLoadModuleOnStack = true;
+        }
+
+        ~NotifyLoadingModule()
+        {
+            PluginModuleMapping::sIsLoadModuleOnStack = false;
+        }
+
+    private:
+        MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+    };
+
+private:
+    void
+    AssociateWithProcessId(base::ProcessId aProcessId)
+    {
+        MOZ_ASSERT(!mProcessIdValid);
+        mProcessId = aProcessId;
+        mProcessIdValid = true;
+    }
+
+    uint32_t mPluginId;
+    bool mProcessIdValid;
+    base::ProcessId mProcessId;
+    PluginModuleContentParent* mModule;
+    bool mChannelOpened;
+
+    friend class NotifyLoadingModule;
+
+    static PRCList sModuleListHead;
+    static bool sIsLoadModuleOnStack;
+};
+
+PRCList PluginModuleMapping::sModuleListHead =
+    PR_INIT_STATIC_CLIST(&PluginModuleMapping::sModuleListHead);
+
+bool PluginModuleMapping::sIsLoadModuleOnStack = false;
 
 /* static */ PluginLibrary*
 PluginModuleContentParent::LoadModule(uint32_t aPluginId)
 {
-    MOZ_ASSERT(!sSavedModuleParent);
+    PluginModuleMapping::NotifyLoadingModule loadingModule;
+    nsAutoPtr<PluginModuleMapping> mapping(new PluginModuleMapping(aPluginId));
+
     MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
 
     /*
      * We send a LoadPlugin message to the chrome process using an intr
      * message. Before it sends its response, it sends a message to create
      * PluginModuleParent instance. That message is handled by
-     * PluginModuleContentParent::Create, which saves the instance in
-     * sSavedModuleParent. We fetch it from there after LoadPlugin finishes.
+     * PluginModuleContentParent::Initialize, which saves the instance in
+     * its module mapping. We fetch it from there after LoadPlugin finishes.
      */
     dom::ContentChild* cp = dom::ContentChild::GetSingleton();
     if (!cp->SendLoadPlugin(aPluginId)) {
         return nullptr;
     }
 
-    PluginModuleContentParent* parent = sSavedModuleParent;
+    PluginModuleContentParent* parent = mapping->GetModule();
     MOZ_ASSERT(parent);
-    sSavedModuleParent = nullptr;
+
+    if (!mapping->IsChannelOpened()) {
+        // mapping is linked into PluginModuleMapping::sModuleListHead and is
+        // needed later, so since this function is returning successfully we
+        // forget it here.
+        mapping.forget();
+    }
 
     return parent;
 }
 
+/* static */ void
+PluginModuleContentParent::AssociatePluginId(uint32_t aPluginId,
+                                             base::ProcessId aProcessId)
+{
+    DebugOnly<PluginModuleMapping*> mapping =
+        PluginModuleMapping::AssociateWithProcessId(aPluginId, aProcessId);
+    MOZ_ASSERT(mapping);
+}
+
 /* static */ PluginModuleContentParent*
-PluginModuleContentParent::Create(mozilla::ipc::Transport* aTransport,
-                                  base::ProcessId aOtherProcess)
+PluginModuleContentParent::Initialize(mozilla::ipc::Transport* aTransport,
+                                      base::ProcessId aOtherProcess)
 {
-    nsAutoPtr<PluginModuleContentParent> parent(new PluginModuleContentParent());
+    nsAutoPtr<PluginModuleMapping> moduleMapping(
+        PluginModuleMapping::Resolve(aOtherProcess));
+    MOZ_ASSERT(moduleMapping);
+    PluginModuleContentParent* parent = moduleMapping->GetModule();
+    MOZ_ASSERT(parent);
+
     ProcessHandle handle;
     if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
         // Bug 1090578 - need to kill |aOtherProcess|, it's boned.
         return nullptr;
     }
 
-    MOZ_ASSERT(!sSavedModuleParent);
-    sSavedModuleParent = parent;
-
     DebugOnly<bool> ok = parent->Open(aTransport, handle, XRE_GetIOMessageLoop(),
                                       mozilla::ipc::ParentSide);
     MOZ_ASSERT(ok);
 
+    moduleMapping->SetChannelOpened();
+
     // Request Windows message deferral behavior on our channel. This
     // applies to the top level and all sub plugin protocols since they
     // all share the same channel.
     parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 
-    return parent.forget();
+    // moduleMapping is linked into PluginModuleMapping::sModuleListHead and is
+    // needed later, so since this function is returning successfully we
+    // forget it here.
+    moduleMapping.forget();
+    return parent;
+}
+
+/* static */ void
+PluginModuleContentParent::OnLoadPluginResult(const uint32_t& aPluginId,
+                                              const bool& aResult)
+{
+    nsAutoPtr<PluginModuleMapping> moduleMapping(
+        PluginModuleMapping::FindModuleByPluginId(aPluginId));
+    MOZ_ASSERT(moduleMapping);
+    PluginModuleContentParent* parent = moduleMapping->GetModule();
+    MOZ_ASSERT(parent);
+    parent->RecvNP_InitializeResult(aResult ? NPERR_NO_ERROR
+                                            : NPERR_GENERIC_ERROR);
+}
+
+void
+PluginModuleChromeParent::SetContentParent(dom::ContentParent* aContentParent)
+{
+    MOZ_ASSERT(aContentParent);
+    mContentParent = aContentParent;
+}
+
+bool
+PluginModuleChromeParent::SendAssociatePluginId()
+{
+    MOZ_ASSERT(mContentParent);
+    return mContentParent->SendAssociatePluginId(mPluginId, OtherSidePID());
 }
 
 // static
 PluginLibrary*
 PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId,
                                      nsPluginTag* aPluginTag)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
 
-    int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
-
-    // Block on the child process being launched and initialized.
     nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId));
+    UniquePtr<LaunchCompleteTask> onLaunchedRunnable(new LaunchedTask(parent));
+    parent->mSubprocess->SetCallRunnableImmediately(!parent->mIsStartingAsync);
     TimeStamp launchStart = TimeStamp::Now();
-    bool launched = parent->mSubprocess->Launch(prefSecs * 1000);
+    bool launched = parent->mSubprocess->Launch(Move(onLaunchedRunnable));
     if (!launched) {
         // We never reached open
         parent->mShutdown = true;
         return nullptr;
     }
+    if (!parent->mIsStartingAsync) {
+        int32_t launchTimeoutSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
+        if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {
+            parent->mShutdown = true;
+            return nullptr;
+        }
+    }
     TimeStamp launchEnd = TimeStamp::Now();
     parent->mTimeBlocked = (launchEnd - launchStart);
-    parent->Open(parent->mSubprocess->GetChannel(),
-                 parent->mSubprocess->GetChildProcessHandle());
+    parent->mIsFlashPlugin = aPluginTag->mIsFlashPlugin;
+    return parent.forget();
+}
+
+void
+PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
+{
+    if (!aSucceeded) {
+        mShutdown = true;
+        OnInitFailure();
+        return;
+    }
+    // We may have already been initialized by another call that was waiting
+    // for process connect. If so, this function doesn't need to run.
+    if (mAsyncInitRv != NS_ERROR_NOT_INITIALIZED || mShutdown) {
+        return;
+    }
+    Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
 
     // Request Windows message deferral behavior on our channel. This
     // applies to the top level and all sub plugin protocols since they
     // all share the same channel.
-    parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+    GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+    TimeoutChanged(CHILD_TIMEOUT_PREF, this);
 
-    TimeoutChanged(CHILD_TIMEOUT_PREF, parent);
+    Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
+    Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
+#ifdef XP_WIN
+    Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
+    Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
+#endif
 
 #ifdef MOZ_CRASHREPORTER
     // If this fails, we're having IPC troubles, and we're doomed anyways.
-    if (!CrashReporterParent::CreateCrashReporter(parent.get())) {
-        parent->Close();
-        return nullptr;
+    if (!CrashReporterParent::CreateCrashReporter(this)) {
+        mShutdown = true;
+        Close();
+        OnInitFailure();
+        return;
     }
 #ifdef XP_WIN
-    mozilla::MutexAutoLock lock(parent->mCrashReporterMutex);
-    parent->mCrashReporter = parent->CrashReporter();
+    { // Scope for lock
+        mozilla::MutexAutoLock lock(mCrashReporterMutex);
+        mCrashReporter = CrashReporter();
+    }
 #endif
 #endif
 
 #ifdef XP_WIN
-    if (aPluginTag->mIsFlashPlugin &&
+    if (mIsFlashPlugin &&
         Preferences::GetBool("dom.ipc.plugins.flash.disable-protected-mode", false)) {
-        parent->SendDisableFlashProtectedMode();
+        SendDisableFlashProtectedMode();
     }
 #endif
 
-    return parent.forget();
+    if (mInitOnAsyncConnect) {
+        mInitOnAsyncConnect = false;
+#if defined(XP_WIN)
+        mAsyncInitRv = NP_GetEntryPoints(mAsyncInitPluginFuncs,
+                                         &mAsyncInitError);
+        if (NS_SUCCEEDED(mAsyncInitRv))
+#endif
+        {
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+            mAsyncInitRv = NP_Initialize(mNPNIface,
+                                         mAsyncInitPluginFuncs,
+                                         &mAsyncInitError);
+#else
+            mAsyncInitRv = NP_Initialize(mNPNIface,
+                                         &mAsyncInitError);
+#endif
+        }
+
+#if defined(XP_MACOSX)
+        if (NS_SUCCEEDED(mAsyncInitRv)) {
+            mAsyncInitRv = NP_GetEntryPoints(mAsyncInitPluginFuncs,
+                                             &mAsyncInitError);
+        }
+#endif
+    }
+}
+
+bool
+PluginModuleChromeParent::WaitForIPCConnection()
+{
+    PluginProcessParent* process = Process();
+    MOZ_ASSERT(process);
+    process->SetCallRunnableImmediately(true);
+    if (!process->WaitUntilConnected()) {
+        return false;
+    }
+    return true;
 }
 
 PluginModuleParent::PluginModuleParent(bool aIsChrome)
     : mIsChrome(aIsChrome)
     , mShutdown(false)
     , mClearSiteDataSupported(false)
     , mGetSitesWithDataSupported(false)
     , mNPNIface(nullptr)
     , mPlugin(nullptr)
     , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
+    , mIsStartingAsync(false)
+    , mNPInitialized(false)
+    , mAsyncNewRv(NS_ERROR_NOT_INITIALIZED)
+    , mAsyncInitPluginFuncs(nullptr)
 {
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
+    mIsStartingAsync = Preferences::GetBool(kAsyncInitPref, false);
+#endif
 }
 
 PluginModuleParent::~PluginModuleParent()
 {
     if (!OkToCleanup()) {
         NS_RUNTIMEABORT("unsafe destruction");
     }
 
@@ -248,26 +531,24 @@ PluginModuleChromeParent::PluginModuleCh
     , mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex")
     , mCrashReporter(nullptr)
 #endif
 #endif
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , mFlashProcess1(0)
     , mFlashProcess2(0)
 #endif
+    , mInitOnAsyncConnect(false)
+    , mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
+    , mAsyncInitError(NPERR_NO_ERROR)
+    , mContentParent(nullptr)
+    , mIsFlashPlugin(false)
 {
     NS_ASSERTION(mSubprocess, "Out of memory!");
 
-    Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
-    Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
-#ifdef XP_WIN
-    Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
-    Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
-#endif
-
     RegisterSettingsCallbacks();
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
 
     mozilla::HangMonitor::RegisterAnnotator(*this);
 }
@@ -942,18 +1223,17 @@ PluginModuleParent::NotifyPluginCrashed(
     if (mPlugin)
         mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
 }
 
 PPluginInstanceParent*
 PluginModuleParent::AllocPPluginInstanceParent(const nsCString& aMimeType,
                                                const uint16_t& aMode,
                                                const InfallibleTArray<nsCString>& aNames,
-                                               const InfallibleTArray<nsCString>& aValues,
-                                               NPError* rv)
+                                               const InfallibleTArray<nsCString>& aValues)
 {
     NS_ERROR("Not reachable!");
     return nullptr;
 }
 
 bool
 PluginModuleParent::DeallocPPluginInstanceParent(PPluginInstanceParent* aActor)
 {
@@ -995,29 +1275,41 @@ PluginModuleParent::SetPluginFuncs(NPPlu
     unused << CallOptionalFunctionsSupported(&urlRedirectSupported,
                                              &mClearSiteDataSupported,
                                              &mGetSitesWithDataSupported);
     if (urlRedirectSupported) {
       aFuncs->urlredirectnotify = NPP_URLRedirectNotify;
     }
 }
 
+#define RESOLVE_AND_CALL(instance, func)                                       \
+NP_BEGIN_MACRO                                                                 \
+    PluginAsyncSurrogate* surrogate = nullptr;                                 \
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);\
+    if (surrogate && (!i || i->UseSurrogate())) {                              \
+        return surrogate->func;                                                \
+    }                                                                          \
+    if (!i) {                                                                  \
+        return NPERR_GENERIC_ERROR;                                            \
+    }                                                                          \
+    return i->func;                                                            \
+NP_END_MACRO
+
 NPError
 PluginModuleParent::NPP_Destroy(NPP instance,
                                 NPSavedData** /*saved*/)
 {
     // FIXME/cjones:
     //  (1) send a "destroy" message to the child
     //  (2) the child shuts down its instance
     //  (3) remove both parent and child IDs from map
     //  (4) free parent
+
     PLUGIN_LOG_DEBUG_FUNCTION;
-
-    PluginInstanceParent* parentInstance =
-        static_cast<PluginInstanceParent*>(instance->pdata);
+    PluginInstanceParent* parentInstance = PluginInstanceParent::Cast(instance);
 
     if (!parentInstance)
         return NPERR_NO_ERROR;
 
     NPError retval = parentInstance->Destroy();
     instance->pdata = nullptr;
 
     unused << PluginInstanceParent::Call__delete__(parentInstance);
@@ -1026,54 +1318,49 @@ PluginModuleParent::NPP_Destroy(NPP inst
 
 NPError
 PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type,
                                   NPStream* stream, NPBool seekable,
                                   uint16_t* stype)
 {
     PROFILER_LABEL("PluginModuleParent", "NPP_NewStream",
       js::ProfileEntry::Category::OTHER);
-
-    PluginInstanceParent* i = InstCast(instance);
-    if (!i)
-        return NPERR_GENERIC_ERROR;
-
-    return i->NPP_NewStream(type, stream, seekable,
-                            stype);
+    RESOLVE_AND_CALL(instance, NPP_NewStream(type, stream, seekable, stype));
 }
 
 NPError
 PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window)
 {
-    PluginInstanceParent* i = InstCast(instance);
-    if (!i)
-        return NPERR_GENERIC_ERROR;
-
-    return i->NPP_SetWindow(window);
+    RESOLVE_AND_CALL(instance, NPP_SetWindow(window));
 }
 
 NPError
 PluginModuleParent::NPP_DestroyStream(NPP instance,
                                       NPStream* stream,
                                       NPReason reason)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return NPERR_GENERIC_ERROR;
 
     return i->NPP_DestroyStream(stream, reason);
 }
 
 int32_t
 PluginModuleParent::NPP_WriteReady(NPP instance,
                                    NPStream* stream)
 {
-    BrowserStreamParent* s = StreamCast(instance, stream);
-    if (!s)
+    PluginAsyncSurrogate* surrogate = nullptr;
+    BrowserStreamParent* s = StreamCast(instance, stream, &surrogate);
+    if (!s) {
+        if (surrogate) {
+            return surrogate->NPP_WriteReady(stream);
+        }
         return -1;
+    }
 
     return s->WriteReady();
 }
 
 int32_t
 PluginModuleParent::NPP_Write(NPP instance,
                               NPStream* stream,
                               int32_t offset,
@@ -1097,62 +1384,60 @@ PluginModuleParent::NPP_StreamAsFile(NPP
         return;
 
     s->StreamAsFile(fname);
 }
 
 void
 PluginModuleParent::NPP_Print(NPP instance, NPPrint* platformPrint)
 {
-    PluginInstanceParent* i = InstCast(instance);
-    if (i)
-        i->NPP_Print(platformPrint);
+
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+    i->NPP_Print(platformPrint);
 }
 
 int16_t
 PluginModuleParent::NPP_HandleEvent(NPP instance, void* event)
 {
-    PluginInstanceParent* i = InstCast(instance);
-    if (!i)
-        return false;
-
-    return i->NPP_HandleEvent(event);
+    RESOLVE_AND_CALL(instance, NPP_HandleEvent(event));
 }
 
 void
 PluginModuleParent::NPP_URLNotify(NPP instance, const char* url,
                                   NPReason reason, void* notifyData)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return;
 
     i->NPP_URLNotify(url, reason, notifyData);
 }
 
 NPError
 PluginModuleParent::NPP_GetValue(NPP instance,
                                  NPPVariable variable, void *ret_value)
 {
-    PluginInstanceParent* i = InstCast(instance);
-    if (!i)
+    // The rules are slightly different for this function.
+    // If there is a surrogate, we *always* use it.
+    PluginAsyncSurrogate* surrogate = nullptr;
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);
+    if (surrogate) {
+        return surrogate->NPP_GetValue(variable, ret_value);
+    }
+    if (!i) {
         return NPERR_GENERIC_ERROR;
-
+    }
     return i->NPP_GetValue(variable, ret_value);
 }
 
 NPError
 PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable,
                                  void *value)
 {
-    PluginInstanceParent* i = InstCast(instance);
-    if (!i)
-        return NPERR_GENERIC_ERROR;
-
-    return i->NPP_SetValue(variable, value);
+    RESOLVE_AND_CALL(instance, NPP_SetValue(variable, value));
 }
 
 bool
 PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
 {
 #ifndef MOZ_X11
     NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
 #else
@@ -1165,47 +1450,31 @@ PluginModuleParent::RecvBackUpXResources
 #endif
     return true;
 }
 
 void
 PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
                                           int32_t status, void* notifyData)
 {
-  PluginInstanceParent* i = InstCast(instance);
+  PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
   if (!i)
     return;
 
   i->NPP_URLRedirectNotify(url, status, notifyData);
 }
 
-PluginInstanceParent*
-PluginModuleParent::InstCast(NPP instance)
+BrowserStreamParent*
+PluginModuleParent::StreamCast(NPP instance, NPStream* s,
+                               PluginAsyncSurrogate** aSurrogate)
 {
-    PluginInstanceParent* ip =
-        static_cast<PluginInstanceParent*>(instance->pdata);
-
-    // If the plugin crashed and the PluginInstanceParent was deleted,
-    // instance->pdata will be nullptr.
-    if (!ip)
+    PluginInstanceParent* ip = PluginInstanceParent::Cast(instance, aSurrogate);
+    if (!ip || (aSurrogate && *aSurrogate && ip->UseSurrogate())) {
         return nullptr;
-
-    if (instance != ip->mNPP) {
-        NS_RUNTIMEABORT("Corrupted plugin data.");
     }
-    return ip;
-}
-
-BrowserStreamParent*
-PluginModuleParent::StreamCast(NPP instance,
-                               NPStream* s)
-{
-    PluginInstanceParent* ip = InstCast(instance);
-    if (!ip)
-        return nullptr;
 
     BrowserStreamParent* sp =
         static_cast<BrowserStreamParent*>(static_cast<AStream*>(s->pdata));
     if (sp->mNPP != ip || s != sp->mStream) {
         NS_RUNTIMEABORT("Corrupted plugin stream data.");
     }
     return sp;
 }
@@ -1214,73 +1483,91 @@ bool
 PluginModuleParent::HasRequiredFunctions()
 {
     return true;
 }
 
 nsresult
 PluginModuleParent::AsyncSetWindow(NPP instance, NPWindow* window)
 {
-    PluginInstanceParent* i = InstCast(instance);
-    if (!i)
+    PluginAsyncSurrogate* surrogate = nullptr;
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);
+    if (surrogate && (!i || i->UseSurrogate())) {
+        return surrogate->AsyncSetWindow(window);
+    } else if (!i) {
         return NS_ERROR_FAILURE;
-
+    }
     return i->AsyncSetWindow(window);
 }
 
 nsresult
 PluginModuleParent::GetImageContainer(NPP instance,
                              mozilla::layers::ImageContainer** aContainer)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     return !i ? NS_ERROR_FAILURE : i->GetImageContainer(aContainer);
 }
 
 nsresult
 PluginModuleParent::GetImageSize(NPP instance,
                                  nsIntSize* aSize)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     return !i ? NS_ERROR_FAILURE : i->GetImageSize(aSize);
 }
 
 nsresult
 PluginModuleParent::SetBackgroundUnknown(NPP instance)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->SetBackgroundUnknown();
 }
 
 nsresult
 PluginModuleParent::BeginUpdateBackground(NPP instance,
                                           const nsIntRect& aRect,
                                           gfxContext** aCtx)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->BeginUpdateBackground(aRect, aCtx);
 }
 
 nsresult
 PluginModuleParent::EndUpdateBackground(NPP instance,
                                         gfxContext* aCtx,
                                         const nsIntRect& aRect)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->EndUpdateBackground(aCtx, aRect);
 }
 
+void
+PluginModuleParent::OnInitFailure()
+{
+    if (GetIPCChannel()->CanSend()) {
+        Close();
+    }
+    /* If we've failed then we need to enumerate any pending NPP_New calls
+       and clean them up. */
+    uint32_t len = mSurrogateInstances.Length();
+    for (uint32_t i = 0; i < len; ++i) {
+        mSurrogateInstances[i]->NotifyAsyncInitFailed();
+    }
+    mSurrogateInstances.Clear();
+}
+
 class OfflineObserver MOZ_FINAL : public nsIObserver
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     explicit OfflineObserver(PluginModuleChromeParent* pmp)
       : mPmp(pmp)
@@ -1385,38 +1672,116 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
+    SetPluginFuncs(pFuncs);
+
     *error = NPERR_NO_ERROR;
-    if (IsChrome()) {
-        PluginSettings settings;
-        GetSettings(&settings);
-        TimeStamp callNpInitStart = TimeStamp::Now();
-        if (!CallNP_Initialize(settings, error)) {
-            Close();
+    return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
+{
+    PLUGIN_LOG_DEBUG_METHOD;
+
+    mNPNIface = bFuncs;
+
+    if (mShutdown) {
+        *error = NPERR_GENERIC_ERROR;
+        return NS_ERROR_FAILURE;
+    }
+
+    mAsyncInitPluginFuncs = pFuncs;
+
+    if (!mSubprocess->IsConnected()) {
+        // The subprocess isn't connected yet. Defer NP_Initialize until
+        // OnProcessLaunched is invoked.
+        mInitOnAsyncConnect = true;
+        *error = NPERR_NO_ERROR;
+        return NS_OK;
+    }
+
+    if (mIsStartingAsync) {
+        PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+    }
+
+    *error = NPERR_NO_ERROR;
+
+    PluginSettings settings;
+    GetSettings(&settings);
+
+    TimeStamp callNpInitStart = TimeStamp::Now();
+    // Asynchronous case
+    if (mIsStartingAsync) {
+        if (!SendAsyncNP_Initialize(settings)) {
             return NS_ERROR_FAILURE;
         }
-        else if (*error != NPERR_NO_ERROR) {
-            Close();
-            return NS_OK;
-        }
         TimeStamp callNpInitEnd = TimeStamp::Now();
         mTimeBlocked += (callNpInitEnd - callNpInitStart);
+        return NS_PLUGIN_INIT_PENDING;
     }
 
-    SetPluginFuncs(pFuncs);
+    // Synchronous case
+    if (!CallNP_Initialize(settings, error)) {
+        Close();
+        return NS_ERROR_FAILURE;
+    }
+    else if (*error != NPERR_NO_ERROR) {
+        Close();
+        return NS_OK;
+    }
+    TimeStamp callNpInitEnd = TimeStamp::Now();
+    mTimeBlocked += (callNpInitEnd - callNpInitStart);
+    RecvNP_InitializeResult(*error);
 
     return NS_OK;
 }
+
+bool
+PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
+{
+    if (aError != NPERR_NO_ERROR) {
+        OnInitFailure();
+        return true;
+    }
+
+    SetPluginFuncs(mAsyncInitPluginFuncs);
+    InitAsyncSurrogates();
+
+    mNPInitialized = true;
+    return true;
+}
+
+bool
+PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
+{
+    if (!mContentParent) {
+        return PluginModuleParent::RecvNP_InitializeResult(aError);
+    }
+    bool initOk = aError == NPERR_NO_ERROR;
+    if (initOk) {
+        SetPluginFuncs(mAsyncInitPluginFuncs);
+        if (SendAssociatePluginId()) {
+            PPluginModule::Bridge(mContentParent, this);
+            mNPInitialized = true;
+        } else {
+            initOk = false;
+        }
+    }
+    return mContentParent->SendLoadPluginResult(mPluginId, initOk);
+}
+
 #else
+
 nsresult
 PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
@@ -1430,49 +1795,133 @@ PluginModuleParent::NP_Initialize(NPNets
 
 nsresult
 PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
 {
     nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
     if (NS_FAILED(rv))
         return rv;
 
+#if defined(XP_MACOSX)
+    if (!mSubprocess->IsConnected()) {
+        // The subprocess isn't connected yet. Defer NP_Initialize until
+        // OnProcessLaunched is invoked.
+        mInitOnAsyncConnect = true;
+        *error = NPERR_NO_ERROR;
+        return NS_OK;
+    }
+#else
+    if (mInitOnAsyncConnect) {
+        *error = NPERR_NO_ERROR;
+        return NS_OK;
+    }
+#endif
+
     PluginSettings settings;
     GetSettings(&settings);
+
     TimeStamp callNpInitStart = TimeStamp::Now();
+    if (mIsStartingAsync) {
+        if (!SendAsyncNP_Initialize(settings)) {
+            return NS_ERROR_FAILURE;
+        }
+        TimeStamp callNpInitEnd = TimeStamp::Now();
+        mTimeBlocked += (callNpInitEnd - callNpInitStart);
+        return NS_PLUGIN_INIT_PENDING;
+    }
+
     if (!CallNP_Initialize(settings, error)) {
         Close();
         return NS_ERROR_FAILURE;
     }
-    if (*error != NPERR_NO_ERROR) {
-        Close();
-        return NS_OK;
-    }
     TimeStamp callNpInitEnd = TimeStamp::Now();
     mTimeBlocked += (callNpInitEnd - callNpInitStart);
+    RecvNP_InitializeResult(*error);
+    return NS_OK;
+}
 
+bool
+PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
+{
+    if (aError != NPERR_NO_ERROR) {
+        OnInitFailure();
+        return true;
+    }
+
+    if (mIsStartingAsync) {
+#if defined(XP_WIN)
+        SetPluginFuncs(mAsyncInitPluginFuncs);
+#endif
+        InitAsyncSurrogates();
+    }
+
+    mNPInitialized = true;
+    return true;
+}
+
+bool
+PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
+{
+    bool ok = true;
+    if (mContentParent) {
+        if ((ok = SendAssociatePluginId())) {
+            PPluginModule::Bridge(mContentParent, this);
+            ok = mContentParent->SendLoadPluginResult(mPluginId,
+                                                      aError == NPERR_NO_ERROR);
+        }
+    } else if (aError == NPERR_NO_ERROR) {
+        // Initialization steps when e10s is disabled
 #if defined XP_WIN
-    // Send the info needed to join the chrome process's audio session to the
-    // plugin process
-    nsID id;
-    nsString sessionName;
-    nsString iconPath;
+        if (mIsStartingAsync) {
+            SetPluginFuncs(mAsyncInitPluginFuncs);
+        }
 
-    if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
-                                                          iconPath)))
-        unused << SendSetAudioSessionData(id, sessionName, iconPath);
+        // Send the info needed to join the chrome process's audio session to the
+        // plugin process
+        nsID id;
+        nsString sessionName;
+        nsString iconPath;
+
+        if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
+                                                              iconPath))) {
+            unused << SendSetAudioSessionData(id, sessionName, iconPath);
+        }
 #endif
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
-    InitializeInjector();
+        InitializeInjector();
+#endif
+    }
+
+    return PluginModuleParent::RecvNP_InitializeResult(aError) && ok;
+}
+
 #endif
 
-    return NS_OK;
+void
+PluginModuleParent::InitAsyncSurrogates()
+{
+    uint32_t len = mSurrogateInstances.Length();
+    for (uint32_t i = 0; i < len; ++i) {
+        NPError err;
+        mAsyncNewRv = mSurrogateInstances[i]->NPP_New(&err);
+        if (NS_FAILED(mAsyncNewRv)) {
+            mSurrogateInstances[i]->NotifyAsyncInitFailed();
+            continue;
+        }
+    }
+    mSurrogateInstances.Clear();
 }
-#endif
+
+bool
+PluginModuleParent::RemovePendingSurrogate(
+                            const nsRefPtr<PluginAsyncSurrogate>& aSurrogate)
+{
+    return mSurrogateInstances.RemoveElement(aSurrogate);
+}
 
 nsresult
 PluginModuleParent::NP_Shutdown(NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
@@ -1515,50 +1964,95 @@ PluginModuleParent::NP_GetValue(void *fu
 }
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
 nsresult
 PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
 {
     NS_ASSERTION(pFuncs, "Null pointer!");
 
+    *error = NPERR_NO_ERROR;
+    if (mIsStartingAsync && !IsChrome()) {
+        PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+        mAsyncInitPluginFuncs = pFuncs;
+    } else {
+        SetPluginFuncs(pFuncs);
+    }
+
+    return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
+{
+#if defined(XP_MACOSX)
+    if (mInitOnAsyncConnect) {
+        PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+        mAsyncInitPluginFuncs = pFuncs;
+        *error = NPERR_NO_ERROR;
+        return NS_OK;
+    }
+#else
+    if (mIsStartingAsync) {
+        PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+    }
+    if (!mSubprocess->IsConnected()) {
+        mAsyncInitPluginFuncs = pFuncs;
+        mInitOnAsyncConnect = true;
+        *error = NPERR_NO_ERROR;
+        return NS_OK;
+    }
+#endif
+
     // We need to have the plugin process update its function table here by
     // actually calling NP_GetEntryPoints. The parent's function table will
     // reflect nullptr entries in the child's table once SetPluginFuncs is
     // called.
 
-    if (IsChrome()) {
-        if (!CallNP_GetEntryPoints(error)) {
-            return NS_ERROR_FAILURE;
-        }
-        else if (*error != NPERR_NO_ERROR) {
-            return NS_OK;
-        }
+    if (!CallNP_GetEntryPoints(error)) {
+        return NS_ERROR_FAILURE;
+    }
+    else if (*error != NPERR_NO_ERROR) {
+        return NS_OK;
     }
 
-    *error = NPERR_NO_ERROR;
-    SetPluginFuncs(pFuncs);
+    return PluginModuleParent::NP_GetEntryPoints(pFuncs, error);
+}
 
-    return NS_OK;
-}
 #endif
 
 nsresult
 PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
                             uint16_t mode, int16_t argc, char* argn[],
                             char* argv[], NPSavedData* saved,
                             NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
+    if (mIsStartingAsync) {
+        if (!PluginAsyncSurrogate::Create(this, pluginType, instance, mode,
+                                          argc, argn, argv)) {
+            *error = NPERR_GENERIC_ERROR;
+            return NS_ERROR_FAILURE;
+        }
+
+        if (!mNPInitialized) {
+            nsRefPtr<PluginAsyncSurrogate> surrogate =
+                PluginAsyncSurrogate::Cast(instance);
+            mSurrogateInstances.AppendElement(surrogate);
+            *error = NPERR_NO_ERROR;
+            return NS_PLUGIN_INIT_PENDING;
+        }
+    }
+
     if (mPluginName.IsEmpty()) {
         GetPluginDetails(mPluginName, mPluginVersion);
         /** mTimeBlocked measures the time that the main thread has been blocked
          *  on plugin module initialization. As implemented, this is the sum of
          *  plugin-container launch + toolhelp32 snapshot + NP_Initialize.
          *  We don't accumulate its value until here because the plugin info
          *  is not available until *after* NP_Initialize.
          */
@@ -1572,46 +2066,83 @@ PluginModuleParent::NPP_New(NPMIMEType p
     InfallibleTArray<nsCString> names;
     InfallibleTArray<nsCString> values;
 
     for (int i = 0; i < argc; ++i) {
         names.AppendElement(NullableString(argn[i]));
         values.AppendElement(NullableString(argv[i]));
     }
 
+    nsresult rv = NPP_NewInternal(pluginType, instance, mode, names, values,
+                                  saved, error);
+    if (NS_FAILED(rv) || !mIsStartingAsync) {
+        return rv;
+    }
+    return NS_PLUGIN_INIT_PENDING;
+}
+
+nsresult
+PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
+                                    uint16_t mode,
+                                    InfallibleTArray<nsCString>& names,
+                                    InfallibleTArray<nsCString>& values,
+                                    NPSavedData* saved, NPError* error)
+{
     PluginInstanceParent* parentInstance =
         new PluginInstanceParent(this, instance,
                                  nsDependentCString(pluginType), mNPNIface);
 
     if (!parentInstance->Init()) {
         delete parentInstance;
         return NS_ERROR_FAILURE;
     }
 
-    instance->pdata = parentInstance;
+    // Release the surrogate reference that was in pdata
+    nsRefPtr<PluginAsyncSurrogate> surrogate(
+        dont_AddRef(PluginAsyncSurrogate::Cast(instance)));
+    // Now replace it with the instance
+    instance->pdata = static_cast<PluginDataResolver*>(parentInstance);
+
+    if (!SendPPluginInstanceConstructor(parentInstance,
+                                        nsDependentCString(pluginType), mode,
+                                        names, values)) {
+        // |parentInstance| is automatically deleted.
+        instance->pdata = nullptr;
+        *error = NPERR_GENERIC_ERROR;
+        return NS_ERROR_FAILURE;
+    }
 
     {   // Scope for timer
         Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_INIT_MS>
             timer(GetHistogramKey());
-        if (!CallPPluginInstanceConstructor(parentInstance,
-                                            nsDependentCString(pluginType), mode,
-                                            names, values, error)) {
-            // |parentInstance| is automatically deleted.
-            instance->pdata = nullptr;
-            // if IPC is down, we'll get an immediate "failed" return, but
-            // without *error being set.  So make sure that the error
-            // condition is signaled to nsNPAPIPluginInstance
-            if (NPERR_NO_ERROR == *error)
+        if (mIsStartingAsync) {
+            MOZ_ASSERT(surrogate);
+            surrogate->AsyncCallDeparting();
+            if (!SendAsyncNPP_New(parentInstance)) {
                 *error = NPERR_GENERIC_ERROR;
-            return NS_ERROR_FAILURE;
+                return NS_ERROR_FAILURE;
+            }
+            *error = NPERR_NO_ERROR;
+        } else {
+            if (!CallSyncNPP_New(parentInstance, error)) {
+                // if IPC is down, we'll get an immediate "failed" return, but
+                // without *error being set.  So make sure that the error
+                // condition is signaled to nsNPAPIPluginInstance
+                if (NPERR_NO_ERROR == *error) {
+                    *error = NPERR_GENERIC_ERROR;
+                }
+                return NS_ERROR_FAILURE;
+            }
         }
     }
 
     if (*error != NPERR_NO_ERROR) {
-        NPP_Destroy(instance, 0);
+        if (!mIsStartingAsync) {
+            NPP_Destroy(instance, 0);
+        }
         return NS_ERROR_FAILURE;
     }
 
     UpdatePluginTimeout();
 
     return NS_OK;
 }
 
@@ -1655,27 +2186,27 @@ PluginModuleParent::NPP_GetSitesWithData
 
     return NS_OK;
 }
 
 #if defined(XP_MACOSX)
 nsresult
 PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->IsRemoteDrawingCoreAnimation(aDrawing);
 }
 
 nsresult
 PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
 {
-    PluginInstanceParent* i = InstCast(instance);
+    PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->ContentsScaleFactorChanged(aContentsScaleFactor);
 }
 #endif // #if defined(XP_MACOSX)
 
 #if defined(MOZ_WIDGET_QT)
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -35,16 +35,17 @@ namespace dom {
 class PCrashReporterParent;
 class CrashReporterParent;
 }
 
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class BrowserStreamParent;
+class PluginAsyncSurrogate;
 class PluginInstanceParent;
 
 #ifdef XP_WIN
 class PluginHangUIParent;
 #endif
 
 /**
  * PluginModuleParent
@@ -74,26 +75,33 @@ protected:
     typedef mozilla::PluginLibrary PluginLibrary;
     typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
     typedef mozilla::dom::CrashReporterParent CrashReporterParent;
 
     PPluginInstanceParent*
     AllocPPluginInstanceParent(const nsCString& aMimeType,
                                const uint16_t& aMode,
                                const InfallibleTArray<nsCString>& aNames,
-                               const InfallibleTArray<nsCString>& aValues,
-                               NPError* rv) MOZ_OVERRIDE;
+                               const InfallibleTArray<nsCString>& aValues)
+                               MOZ_OVERRIDE;
 
     virtual bool
     DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) MOZ_OVERRIDE;
 
 public:
     explicit PluginModuleParent(bool aIsChrome);
     virtual ~PluginModuleParent();
 
+    bool RemovePendingSurrogate(const nsRefPtr<PluginAsyncSurrogate>& aSurrogate);
+
+    /** @return the state of the pref that controls async plugin init */
+    bool IsStartingAsync() const { return mIsStartingAsync; }
+    /** @return whether this modules NP_Initialize has successfully completed
+        executing */
+    bool IsInitialized() const { return mNPInitialized; }
     bool IsChrome() const { return mIsChrome; }
 
     virtual void SetPlugin(nsNPAPIPlugin* plugin) MOZ_OVERRIDE
     {
         mPlugin = plugin;
     }
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
@@ -103,16 +111,18 @@ public:
     }
 
     bool OkToCleanup() const {
         return !IsOnCxxStack();
     }
 
     void ProcessRemoteNativeEventsInInterruptCall();
 
+    virtual bool WaitForIPCConnection() { return true; }
+
     nsCString GetHistogramKey() const {
         return mPluginName + mPluginVersion;
     }
 
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
     MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
@@ -154,26 +164,34 @@ protected:
     RecvPopCursor() MOZ_OVERRIDE;
 
     virtual bool
     RecvNPN_SetException(const nsCString& aMessage) MOZ_OVERRIDE;
 
     virtual bool
     RecvNPN_ReloadPlugins(const bool& aReloadPages) MOZ_OVERRIDE;
 
-    static PluginInstanceParent* InstCast(NPP instance);
-    static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
+    virtual bool
+    RecvNP_InitializeResult(const NPError& aError) MOZ_OVERRIDE;
+
+    static BrowserStreamParent* StreamCast(NPP instance, NPStream* s,
+                                           PluginAsyncSurrogate** aSurrogate = nullptr);
 
 protected:
     virtual void UpdatePluginTimeout() {}
 
     virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE { return true; }
 
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
+    nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, uint16_t mode,
+                             InfallibleTArray<nsCString>& names,
+                             InfallibleTArray<nsCString>& values,
+                             NPSavedData* saved, NPError* error);
+
     // NPP-like API that Gecko calls are trampolined into.  These 
     // messages then get forwarded along to the plugin instance,
     // and then eventually the child process.
 
     static NPError NPP_Destroy(NPP instance, NPSavedData** save);
 
     static NPError NPP_SetWindow(NPP instance, NPWindow* window);
     static NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
@@ -230,27 +248,30 @@ protected:
                                        uint64_t maxAge);
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
     virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
 #endif
 
+    void InitAsyncSurrogates();
+
 protected:
     void NotifyPluginCrashed();
+    void OnInitFailure();
 
     bool GetSetting(NPNVariable aVariable);
     void GetSettings(PluginSettings* aSettings);
 
     bool mIsChrome;
     bool mShutdown;
     bool mClearSiteDataSupported;
     bool mGetSitesWithDataSupported;
-    const NPNetscapeFuncs* mNPNIface;
+    NPNetscapeFuncs* mNPNIface;
     nsNPAPIPlugin* mPlugin;
     ScopedMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
     nsRefPtr<nsIObserver> mProfilerObserver;
     TimeDuration mTimeBlocked;
     nsCString mPluginName;
@@ -261,28 +282,39 @@ protected:
     // object instead of the plugin process's lifetime
     ScopedClose mPluginXSocketFdDup;
 #endif
 
     bool
     GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
 
     friend class mozilla::dom::CrashReporterParent;
+    friend class mozilla::plugins::PluginAsyncSurrogate;
+
+    bool              mIsStartingAsync;
+    bool              mNPInitialized;
+    nsTArray<nsRefPtr<PluginAsyncSurrogate>> mSurrogateInstances;
+    nsresult          mAsyncNewRv;
+    NPPluginFuncs*    mAsyncInitPluginFuncs;
 };
 
 class PluginModuleContentParent : public PluginModuleParent
 {
   public:
+    explicit PluginModuleContentParent();
+
     static PluginLibrary* LoadModule(uint32_t aPluginId);
 
-    static PluginModuleContentParent* Create(mozilla::ipc::Transport* aTransport,
-                                             base::ProcessId aOtherProcess);
+    static PluginModuleContentParent* Initialize(mozilla::ipc::Transport* aTransport,
+                                                 base::ProcessId aOtherProcess);
+
+    static void OnLoadPluginResult(const uint32_t& aPluginId, const bool& aResult);
+    static void AssociatePluginId(uint32_t aPluginId, base::ProcessId aProcessId);
 
   private:
-    explicit PluginModuleContentParent();
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     void OnCrash(DWORD processID) MOZ_OVERRIDE {}
 #endif
 
     static PluginModuleContentParent* sSavedModuleParent;
 };
 
@@ -308,16 +340,27 @@ class PluginModuleChromeParent
     /**
      * Called by Plugin Hang UI to notify that the user has clicked continue.
      * Used for chrome hang annotations.
      */
     void
     OnHangUIContinue();
 #endif // XP_WIN
 
+    virtual bool WaitForIPCConnection() MOZ_OVERRIDE;
+
+    virtual bool
+    RecvNP_InitializeResult(const NPError& aError) MOZ_OVERRIDE;
+
+    void
+    SetContentParent(dom::ContentParent* aContentParent);
+
+    bool
+    SendAssociatePluginId();
+
     void CachedSettingChanged();
 
 private:
     virtual void
     EnteredCxxStack() MOZ_OVERRIDE;
 
     void
     ExitedCxxStack() MOZ_OVERRIDE;
@@ -336,18 +379,24 @@ private:
     AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
                               uint32_t* processType) MOZ_OVERRIDE;
     virtual bool
     DeallocPCrashReporterParent(PCrashReporterParent* actor) MOZ_OVERRIDE;
 
     PluginProcessParent* Process() const { return mSubprocess; }
     base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
 
-#if !defined(XP_UNIX) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK)
-    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) MOZ_OVERRIDE;
+#else
+    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) MOZ_OVERRIDE;
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+    virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) MOZ_OVERRIDE;
 #endif
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
     // aFilePath is UTF8, not native!
     explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId);
 
     CrashReporterParent* CrashReporter();
@@ -416,25 +465,53 @@ private:
     /**
      * Finishes the Plugin Hang UI and cancels if it is being shown to the user.
      */
     void
     FinishHangUI();
 #endif
 
     friend class mozilla::dom::CrashReporterParent;
+    friend class mozilla::plugins::PluginAsyncSurrogate;
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     void InitializeInjector();
-    
+
     void OnCrash(DWORD processID) MOZ_OVERRIDE;
 
     DWORD mFlashProcess1;
     DWORD mFlashProcess2;
 #endif
 
+    void OnProcessLaunched(const bool aSucceeded);
+
+    class LaunchedTask : public LaunchCompleteTask
+    {
+    public:
+        explicit LaunchedTask(PluginModuleChromeParent* aModule)
+            : mModule(aModule)
+        {
+            MOZ_ASSERT(aModule);
+        }
+
+        void Run() MOZ_OVERRIDE
+        {
+            mModule->OnProcessLaunched(mLaunchSucceeded);
+        }
+
+    private:
+        PluginModuleChromeParent* mModule;
+    };
+
+    friend class LaunchedTask;
+
+    bool                mInitOnAsyncConnect;
+    nsresult            mAsyncInitRv;
+    NPError             mAsyncInitError;
+    dom::ContentParent* mContentParent;
     nsCOMPtr<nsIObserver> mOfflineObserver;
+    bool mIsFlashPlugin;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // mozilla_plugins_PluginModuleParent_h
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -124,16 +124,17 @@ PluginProcessParent::WaitUntilConnected(
     return result;
 }
 
 void
 PluginProcessParent::OnChannelConnected(int32_t peer_pid)
 {
     GeckoChildProcessHost::OnChannelConnected(peer_pid);
     if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
+        mLaunchCompleteTask->SetLaunchSucceeded();
         mMainMsgLoop->PostTask(FROM_HERE, mLaunchCompleteTask.release());
     }
 }
 
 void
 PluginProcessParent::OnChannelError()
 {
     GeckoChildProcessHost::OnChannelError();
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2362,16 +2362,18 @@ pref("dom.ipc.plugins.flash.disable-prot
 
 pref("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", true);
 pref("dom.ipc.plugins.reportCrashURL", true);
 
 // How long we wait before unloading an idle plugin process.
 // Defaults to 30 seconds.
 pref("dom.ipc.plugins.unloadTimeoutSecs", 30);
 
+pref("dom.ipc.plugins.asyncInit", false);
+
 pref("dom.ipc.processCount", 1);
 
 // Enable caching of Moz2D Path objects for SVG geometry elements
 pref("svg.path-caching.enabled", true);
 
 // Enable the use of display-lists for SVG hit-testing and painting.
 pref("svg.display-lists.hit-testing.enabled", true);
 pref("svg.display-lists.painting.enabled", true);