Merge m-c to fx-team a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 30 Dec 2014 16:06:29 -0800
changeset 247555 edccc126caae2f70cfd6b3ed8487d90383908ec0
parent 247554 5f6fb643ebee868ee6dc2f81800958f373bde94c (current diff)
parent 247514 88037f94b7d77d58f73b9b58d7c1c6235a966ca9 (diff)
child 247556 73b35d5c99166acf16a751d3f70f4f9ad1c76f7a
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)
reviewersmerge
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
Merge m-c to fx-team a=merge
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -393,24 +393,24 @@ var shell = {
         type = 'menu-button';
         break;
       case evt.DOM_VK_F1: // headset button
         type = 'headset-button';
         break;
     }
 
     let mediaKeys = {
-      'MediaNextTrack': 'media-next-track-button',
-      'MediaPreviousTrack': 'media-previous-track-button',
+      'MediaTrackNext': 'media-next-track-button',
+      'MediaTrackPrevious': 'media-previous-track-button',
       'MediaPause': 'media-pause-button',
       'MediaPlay': 'media-play-button',
       'MediaPlayPause': 'media-play-pause-button',
       'MediaStop': 'media-stop-button',
       'MediaRewind': 'media-rewind-button',
-      'FastFwd': 'media-fast-forward-button'
+      'MediaFastForward': 'media-fast-forward-button'
     };
 
     let isMediaKey = false;
     if (mediaKeys[evt.key]) {
       isMediaKey = true;
       type = mediaKeys[evt.key];
     }
 
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "f1830710359dad095bf5dec65a70c138b6e9e57a", 
+    "revision": "e704a7447a7fce238197a7f5429b2282090f1d13", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="322ef5116a5827a30c9a3cd9b842449a9c66a5b3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="26d479f0fccb7174e06255121e4e938c1b280d67"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a2e84985476d4b1fa518b7465a26cfe596923e7"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -528,16 +528,17 @@
 @BINPATH@/components/webvtt.xpt
 @BINPATH@/components/WebVTT.manifest
 @BINPATH@/components/WebVTTParserWrapper.js
 #ifdef MOZ_NFC
 @BINPATH@/components/nsNfc.manifest
 @BINPATH@/components/nsNfc.js
 @BINPATH@/components/Nfc.manifest
 @BINPATH@/components/Nfc.js
+@BINPATH@/components/NfcContentHelper.manifest
 @BINPATH@/components/NfcContentHelper.js
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/nsPrompter.manifest
--- a/browser/devtools/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
@@ -19,24 +19,24 @@ let test = asyncTest(function* () {
 
   let hud = yield openConsole();
 
   let results = yield waitForMessages({
     webconsole: hud,
     messages: [
       {
         name: "Logged mixed active content",
-        text: "Loading mixed (insecure) active content on a secure page \"http://example.com/\"",
+        text: "Loading mixed (insecure) active content \"http://example.com/\" on a secure page",
         category: CATEGORY_SECURITY,
         severity: SEVERITY_WARNING,
         objects: true,
       },
       {
         name: "Logged mixed passive content - image",
-        text: "Loading mixed (insecure) display content on a secure page \"http://example.com/tests/image/test/mochitest/blue.png\"",
+        text: "Loading mixed (insecure) display content \"http://example.com/tests/image/test/mochitest/blue.png\" on a secure page",
         category: CATEGORY_SECURITY,
         severity: SEVERITY_WARNING,
         objects: true,
       },
     ],
   });
 
   testClickOpenNewTab(hud, results);
--- a/browser/devtools/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -90,26 +90,27 @@ function afterNotificationShown(hud, not
   PopupNotifications.panel.firstChild.disableMixedContentProtection();
   notification.remove();
 
   waitForMessages({
     webconsole: hud,
     messages: [
       {
         name: "Logged blocking mixed active content",
-        text: "Loading mixed (insecure) active content on a secure"+
-          " page \"http://example.com/\"",
+        text: "Loading mixed (insecure) active content \"http://example.com/\"" +
+          " on a secure page",
         category: CATEGORY_SECURITY,
         severity: SEVERITY_WARNING,
         objects: true,
       },
       {
         name: "Logged blocking mixed passive content - image",
-        text: "Loading mixed (insecure) display content on a secure page"+
-          " \"http://example.com/tests/image/test/mochitest/blue.png\"",
+        text: "Loading mixed (insecure) display content" +
+          " \"http://example.com/tests/image/test/mochitest/blue.png\"" +
+          " on a secure page",
         category: CATEGORY_SECURITY,
         severity: SEVERITY_WARNING,
         objects: true,
       },
     ],
   }).then(msgs => deferred.resolve(msgs), Cu.reportError);
 }
 
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -13,16 +13,17 @@ import re
 import select
 import shutil
 import signal
 import subprocess
 import sys
 import threading
 import tempfile
 import sqlite3
+import zipfile
 from datetime import datetime, timedelta
 from string import Template
 
 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
 sys.path.insert(0, SCRIPT_DIR)
 import automationutils
 
 # --------------------------------------------------------------
@@ -917,17 +918,17 @@ class Automation(object):
 
     installRDFFilename = "install.rdf"
 
     extensionsRootDir = os.path.join(profileDir, "extensions", "staged")
     if not os.path.isdir(extensionsRootDir):
       os.makedirs(extensionsRootDir)
 
     if os.path.isfile(extensionSource):
-      reader = automationutils.ZipFileReader(extensionSource)
+      reader = zipfile.ZipFile(extensionSource, "r")
 
       for filename in reader.namelist():
         # Sanity check the zip file.
         if os.path.isabs(filename):
           self.log.info("INFO | automation.py | Cannot install extension, bad files in xpi")
           return
 
         # We may need to dig the extensionID out of the zip file...
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -8,21 +8,19 @@ import logging
 from operator import itemgetter
 import os
 import platform
 import re
 import signal
 import subprocess
 import sys
 import tempfile
-import zipfile
 import mozinfo
 
 __all__ = [
-  "ZipFileReader",
   "dumpLeakLog",
   "processLeakLog",
   'systemMemory',
   'environment',
   'dumpScreen',
   "ShutdownLeaks",
   "setAutomationLog",
   ]
@@ -35,78 +33,16 @@ def resetGlobalLog():
   log.setLevel(logging.INFO)
   log.addHandler(handler)
 resetGlobalLog()
 
 def setAutomationLog(alt_logger):
   global log
   log = alt_logger
 
-class ZipFileReader(object):
-  """
-  Class to read zip files in Python 2.5 and later. Limited to only what we
-  actually use.
-  """
-
-  def __init__(self, filename):
-    self._zipfile = zipfile.ZipFile(filename, "r")
-
-  def __del__(self):
-    self._zipfile.close()
-
-  def _getnormalizedpath(self, path):
-    """
-    Gets a normalized path from 'path' (or the current working directory if
-    'path' is None). Also asserts that the path exists.
-    """
-    if path is None:
-      path = os.curdir
-    path = os.path.normpath(os.path.expanduser(path))
-    assert os.path.isdir(path)
-    return path
-
-  def _extractname(self, name, path):
-    """
-    Extracts a file with the given name from the zip file to the given path.
-    Also creates any directories needed along the way.
-    """
-    filename = os.path.normpath(os.path.join(path, name))
-    if name.endswith("/"):
-      os.makedirs(filename)
-    else:
-      path = os.path.split(filename)[0]
-      if not os.path.isdir(path):
-        os.makedirs(path)
-      with open(filename, "wb") as dest:
-        dest.write(self._zipfile.read(name))
-
-  def namelist(self):
-    return self._zipfile.namelist()
-
-  def read(self, name):
-    return self._zipfile.read(name)
-
-  def extract(self, name, path = None):
-    if hasattr(self._zipfile, "extract"):
-      return self._zipfile.extract(name, path)
-
-    # This will throw if name is not part of the zip file.
-    self._zipfile.getinfo(name)
-
-    self._extractname(name, self._getnormalizedpath(path))
-
-  def extractall(self, path = None):
-    if hasattr(self._zipfile, "extractall"):
-      return self._zipfile.extractall(path)
-
-    path = self._getnormalizedpath(path)
-
-    for name in self._zipfile.namelist():
-      self._extractname(name, path)
-
 # Python does not provide strsignal() even in the very latest 3.x.
 # This is a reasonable fake.
 def strsig(n):
   # Signal numbers run 0 through NSIG-1; an array with NSIG members
   # has exactly that many slots
   _sigtbl = [None]*signal.NSIG
   for k in dir(signal):
     if k.startswith("SIG") and not k.startswith("SIG_") and k != "SIGCLD" and k != "SIGPOLL":
--- a/configure.in
+++ b/configure.in
@@ -7240,17 +7240,17 @@ AC_SUBST(MOZ_REPLACE_MALLOC)
 AC_SUBST(MOZ_REPLACE_MALLOC_LINKAGE)
 
 dnl ========================================================
 dnl = Jemalloc build setup
 dnl ========================================================
 if test -z "$MOZ_MEMORY"; then
   if test -n "$MOZ_JEMALLOC3" -a -z "$MOZ_REPLACE_MALLOC"; then
     MOZ_NATIVE_JEMALLOC=1
-    AC_CHECK_FUNCS(mallctl nallocm,,
+    AC_CHECK_FUNCS(mallctl nallocx,,
       [MOZ_NATIVE_JEMALLOC=
        break])
     if test -n "$MOZ_NATIVE_JEMALLOC"; then
       MOZ_MEMORY=1
       AC_DEFINE(MOZ_MEMORY)
       AC_DEFINE(MOZ_JEMALLOC3)
       AC_DEFINE(MOZ_NATIVE_JEMALLOC)
     fi
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -35,34 +35,9 @@ DEPRECATED_OPERATION(UseOfCaptureEvents)
 DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(Window_Content)
 DEPRECATED_OPERATION(SyncXMLHttpRequest)
 DEPRECATED_OPERATION(DataContainerEvent)
 DEPRECATED_OPERATION(SendAsBinary)
 DEPRECATED_OPERATION(Window_Controllers)
-DEPRECATED_OPERATION(KeyNameDown)
-DEPRECATED_OPERATION(KeyNameLeft)
-DEPRECATED_OPERATION(KeyNameRight)
-DEPRECATED_OPERATION(KeyNameUp)
-DEPRECATED_OPERATION(KeyNameCrsel)
-DEPRECATED_OPERATION(KeyNameDel)
-DEPRECATED_OPERATION(KeyNameExsel)
-DEPRECATED_OPERATION(KeyNameMenu)
-DEPRECATED_OPERATION(KeyNameEsc)
-DEPRECATED_OPERATION(KeyNameNonconvert)
-DEPRECATED_OPERATION(KeyNameHalfWidth)
-DEPRECATED_OPERATION(KeyNameRomanCharacters)
-DEPRECATED_OPERATION(KeyNameFullWidth)
-DEPRECATED_OPERATION(KeyNameSelectMedia)
-DEPRECATED_OPERATION(KeyNameMediaNextTrack)
-DEPRECATED_OPERATION(KeyNameMediaPreviousTrack)
-DEPRECATED_OPERATION(KeyNameRed)
-DEPRECATED_OPERATION(KeyNameGreen)
-DEPRECATED_OPERATION(KeyNameYellow)
-DEPRECATED_OPERATION(KeyNameBlue)
-DEPRECATED_OPERATION(KeyNameLive)
-DEPRECATED_OPERATION(KeyNameApps)
-DEPRECATED_OPERATION(KeyNameFastFwd)
-DEPRECATED_OPERATION(KeyNameZoom)
-DEPRECATED_OPERATION(KeyNameDeadKeys)
 DEPRECATED_OPERATION(ImportXULIntoContent)
--- a/dom/events/KeyNameList.h
+++ b/dom/events/KeyNameList.h
@@ -41,129 +41,129 @@ DEFINE_KEYNAME_WITH_SAME_NAME(Fn)
 // DEFINE_KEYNAME_WITH_SAME_NAME(FnLock)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Hyper)
 DEFINE_KEYNAME_WITH_SAME_NAME(Meta)
 DEFINE_KEYNAME_WITH_SAME_NAME(NumLock)
 DEFINE_KEYNAME_WITH_SAME_NAME(OS)
 DEFINE_KEYNAME_WITH_SAME_NAME(ScrollLock)
 DEFINE_KEYNAME_WITH_SAME_NAME(Shift)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Super)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Symbol)
+DEFINE_KEYNAME_WITH_SAME_NAME(Symbol)
 // DEFINE_KEYNAME_WITH_SAME_NAME(SymbolLock)
 
 /******************************************************************************
  * Whitespace Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(Enter)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Separator)
 DEFINE_KEYNAME_WITH_SAME_NAME(Tab)
 
 /******************************************************************************
  * Navigation Keys
  *****************************************************************************/
-DEFINE_KEYNAME_WITH_SAME_NAME(Down) // Rename to ArrowDown
-DEFINE_KEYNAME_WITH_SAME_NAME(Left) // Rename to ArrowLeft
-DEFINE_KEYNAME_WITH_SAME_NAME(Right) // Rename to ArrowRight
-DEFINE_KEYNAME_WITH_SAME_NAME(Up) // Rename to ArrowUp
+DEFINE_KEYNAME_WITH_SAME_NAME(ArrowDown)
+DEFINE_KEYNAME_WITH_SAME_NAME(ArrowLeft)
+DEFINE_KEYNAME_WITH_SAME_NAME(ArrowRight)
+DEFINE_KEYNAME_WITH_SAME_NAME(ArrowUp)
 DEFINE_KEYNAME_WITH_SAME_NAME(End)
 DEFINE_KEYNAME_WITH_SAME_NAME(Home)
 DEFINE_KEYNAME_WITH_SAME_NAME(PageDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(PageUp)
 
 /******************************************************************************
  * Editing Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(Backspace)
 DEFINE_KEYNAME_WITH_SAME_NAME(Clear)
 DEFINE_KEYNAME_WITH_SAME_NAME(Copy)
-DEFINE_KEYNAME_WITH_SAME_NAME(Crsel) // Rename to CrSel
+DEFINE_KEYNAME_WITH_SAME_NAME(CrSel)
 DEFINE_KEYNAME_WITH_SAME_NAME(Cut)
-DEFINE_KEYNAME_WITH_SAME_NAME(Del) // Rename to Delete
+DEFINE_KEYNAME_WITH_SAME_NAME(Delete)
 DEFINE_KEYNAME_WITH_SAME_NAME(EraseEof)
-DEFINE_KEYNAME_WITH_SAME_NAME(Exsel) // Rename to ExSel
+DEFINE_KEYNAME_WITH_SAME_NAME(ExSel)
 DEFINE_KEYNAME_WITH_SAME_NAME(Insert)
 DEFINE_KEYNAME_WITH_SAME_NAME(Paste)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Redo)
+DEFINE_KEYNAME_WITH_SAME_NAME(Redo)
 DEFINE_KEYNAME_WITH_SAME_NAME(Undo)
 
 /******************************************************************************
  * UI Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(Accept)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Again)
 DEFINE_KEYNAME_WITH_SAME_NAME(Attn)
 DEFINE_KEYNAME_WITH_SAME_NAME(Cancel)
-DEFINE_KEYNAME_WITH_SAME_NAME(Menu) // Rename to ContextMenu
-DEFINE_KEYNAME_WITH_SAME_NAME(Esc) // Rename to Escape
+DEFINE_KEYNAME_WITH_SAME_NAME(ContextMenu)
+DEFINE_KEYNAME_WITH_SAME_NAME(Escape)
 DEFINE_KEYNAME_WITH_SAME_NAME(Execute)
 DEFINE_KEYNAME_WITH_SAME_NAME(Find)
 DEFINE_KEYNAME_WITH_SAME_NAME(Help)
 DEFINE_KEYNAME_WITH_SAME_NAME(Pause)
 DEFINE_KEYNAME_WITH_SAME_NAME(Play)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Props)
 DEFINE_KEYNAME_WITH_SAME_NAME(Select)
-// DEFINE_KEYNAME_WITH_SAME_NAME(ZoomIn)
-// DEFINE_KEYNAME_WITH_SAME_NAME(ZoomOut)
+DEFINE_KEYNAME_WITH_SAME_NAME(ZoomIn)
+DEFINE_KEYNAME_WITH_SAME_NAME(ZoomOut)
 
 /******************************************************************************
  * Device Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(BrightnessDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(BrightnessUp)
 DEFINE_KEYNAME_WITH_SAME_NAME(Camera)
 DEFINE_KEYNAME_WITH_SAME_NAME(Eject)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LogOff)
+DEFINE_KEYNAME_WITH_SAME_NAME(LogOff)
 DEFINE_KEYNAME_WITH_SAME_NAME(Power)
-// DEFINE_KEYNAME_WITH_SAME_NAME(PowerOff)
+DEFINE_KEYNAME_WITH_SAME_NAME(PowerOff)
 DEFINE_KEYNAME_WITH_SAME_NAME(PrintScreen)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Hibernate)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Standby)
-// DEFINE_KEYNAME_WITH_SAME_NAME(WakeUp)
+DEFINE_KEYNAME_WITH_SAME_NAME(Hibernate)
+DEFINE_KEYNAME_WITH_SAME_NAME(Standby)
+DEFINE_KEYNAME_WITH_SAME_NAME(WakeUp)
 
 /******************************************************************************
  * IME and Composition Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(AllCandidates)
 DEFINE_KEYNAME_WITH_SAME_NAME(Alphanumeric)
 DEFINE_KEYNAME_WITH_SAME_NAME(CodeInput)
 DEFINE_KEYNAME_WITH_SAME_NAME(Compose)
 DEFINE_KEYNAME_WITH_SAME_NAME(Convert)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Dead)
+DEFINE_KEYNAME_WITH_SAME_NAME(Dead)
 DEFINE_KEYNAME_WITH_SAME_NAME(FinalMode)
-// DEFINE_KEYNAME_WITH_SAME_NAME(GroupFirst)
-// DEFINE_KEYNAME_WITH_SAME_NAME(GroupLast)
-// DEFINE_KEYNAME_WITH_SAME_NAME(GroupNext)
-// DEFINE_KEYNAME_WITH_SAME_NAME(GroupPrevious)
+DEFINE_KEYNAME_WITH_SAME_NAME(GroupFirst)
+DEFINE_KEYNAME_WITH_SAME_NAME(GroupLast)
+DEFINE_KEYNAME_WITH_SAME_NAME(GroupNext)
+DEFINE_KEYNAME_WITH_SAME_NAME(GroupPrevious)
 DEFINE_KEYNAME_WITH_SAME_NAME(ModeChange)
 // DEFINE_KEYNAME_WITH_SAME_NAME(NextCandidate)
-DEFINE_KEYNAME_WITH_SAME_NAME(Nonconvert) // Rename to NonConvert
+DEFINE_KEYNAME_WITH_SAME_NAME(NonConvert)
 DEFINE_KEYNAME_WITH_SAME_NAME(PreviousCandidate)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Process)
-// DEFINE_KEYNAME_WITH_SAME_NAME(SingleCandidate)
+DEFINE_KEYNAME_WITH_SAME_NAME(SingleCandidate)
 
 /******************************************************************************
  * Keys specific to Korean keyboards
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(HangulMode)
 DEFINE_KEYNAME_WITH_SAME_NAME(HanjaMode)
 DEFINE_KEYNAME_WITH_SAME_NAME(JunjaMode)
 
 /******************************************************************************
  * Keys specific to Japanese keyboards
  *****************************************************************************/
-// DEFINE_KEYNAME_WITH_SAME_NAME(Eisu)
-DEFINE_KEYNAME_WITH_SAME_NAME(HalfWidth) // Rename to Hankaku
+DEFINE_KEYNAME_WITH_SAME_NAME(Eisu)
+DEFINE_KEYNAME_WITH_SAME_NAME(Hankaku)
 DEFINE_KEYNAME_WITH_SAME_NAME(Hiragana)
-// DEFINE_KEYNAME_WITH_SAME_NAME(HiraganaKatakana)
+DEFINE_KEYNAME_WITH_SAME_NAME(HiraganaKatakana)
 DEFINE_KEYNAME_WITH_SAME_NAME(KanaMode)
 DEFINE_KEYNAME_WITH_SAME_NAME(KanjiMode)
 DEFINE_KEYNAME_WITH_SAME_NAME(Katakana)
-DEFINE_KEYNAME_WITH_SAME_NAME(RomanCharacters) // Rename to Romaji
-DEFINE_KEYNAME_WITH_SAME_NAME(FullWidth) // Rename to Zenkaku
-// DEFINE_KEYNAME_WITH_SAME_NAME(ZenkakuHankaku)
+DEFINE_KEYNAME_WITH_SAME_NAME(Romaji)
+DEFINE_KEYNAME_WITH_SAME_NAME(Zenkaku)
+DEFINE_KEYNAME_WITH_SAME_NAME(ZenkakuHankaku)
 
 /******************************************************************************
  * General-Purpose Function Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(F1)
 DEFINE_KEYNAME_WITH_SAME_NAME(F2)
 DEFINE_KEYNAME_WITH_SAME_NAME(F3)
 DEFINE_KEYNAME_WITH_SAME_NAME(F4)
@@ -201,48 +201,48 @@ DEFINE_KEYNAME_WITH_SAME_NAME(F35)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Soft1)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Soft2)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Soft3)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Soft4)
 
 /******************************************************************************
  * Multimedia Keys
  *****************************************************************************/
-// DEFINE_KEYNAME_WITH_SAME_NAME(Close)
-// DEFINE_KEYNAME_WITH_SAME_NAME(MailForward)
-// DEFINE_KEYNAME_WITH_SAME_NAME(MailReply)
-// DEFINE_KEYNAME_WITH_SAME_NAME(MailSend)
+DEFINE_KEYNAME_WITH_SAME_NAME(Close)
+DEFINE_KEYNAME_WITH_SAME_NAME(MailForward)
+DEFINE_KEYNAME_WITH_SAME_NAME(MailReply)
+DEFINE_KEYNAME_WITH_SAME_NAME(MailSend)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaPlayPause)
-DEFINE_KEYNAME_WITH_SAME_NAME(SelectMedia) // Rename to MediaSelect
+DEFINE_KEYNAME_WITH_SAME_NAME(MediaSelect)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaStop)
-DEFINE_KEYNAME_WITH_SAME_NAME(MediaNextTrack) // Rename to MediaTrackNext
-DEFINE_KEYNAME_WITH_SAME_NAME(MediaPreviousTrack) // Rename to MediaTrackPrevious
-// DEFINE_KEYNAME_WITH_SAME_NAME(New)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Open)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Print)
-// DEFINE_KEYNAME_WITH_SAME_NAME(Save)
-// DEFINE_KEYNAME_WITH_SAME_NAME(SpellCheck)
+DEFINE_KEYNAME_WITH_SAME_NAME(MediaTrackNext)
+DEFINE_KEYNAME_WITH_SAME_NAME(MediaTrackPrevious)
+DEFINE_KEYNAME_WITH_SAME_NAME(New)
+DEFINE_KEYNAME_WITH_SAME_NAME(Open)
+DEFINE_KEYNAME_WITH_SAME_NAME(Print)
+DEFINE_KEYNAME_WITH_SAME_NAME(Save)
+DEFINE_KEYNAME_WITH_SAME_NAME(SpellCheck)
 DEFINE_KEYNAME_WITH_SAME_NAME(VolumeDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(VolumeUp)
 DEFINE_KEYNAME_WITH_SAME_NAME(VolumeMute)
 
 /******************************************************************************
  * Application Keys
  *****************************************************************************/
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchCalculator)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchCalendar)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchCalculator)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchCalendar)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMail)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMediaPlayer)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMusicPlayer)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMyComputer)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchScreenSaver)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchSpreadsheet)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchWebBrowser)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchWebCam)
-// DEFINE_KEYNAME_WITH_SAME_NAME(LaunchWordProcessor)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMediaPlayer)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMusicPlayer)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchMyComputer)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchScreenSaver)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchSpreadsheet)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchWebBrowser)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchWebCam)
+DEFINE_KEYNAME_WITH_SAME_NAME(LaunchWordProcessor)
 
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication1)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication2)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication3)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication4)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication5)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication6)
 DEFINE_KEYNAME_WITH_SAME_NAME(LaunchApplication7)
@@ -274,53 +274,53 @@ DEFINE_KEYNAME_WITH_SAME_NAME(BrowserSto
  *****************************************************************************/
 // DEFINE_KEYNAME_WITH_SAME_NAME(AudioBalanceLeft)
 // DEFINE_KEYNAME_WITH_SAME_NAME(AudioBalanceRight)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioBassBoostDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioBassBoostUp)
 // DEFINE_KEYNAME_WITH_SAME_NAME(AudioFaderFront)
 // DEFINE_KEYNAME_WITH_SAME_NAME(AudioFaderRear)
 // DEFINE_KEYNAME_WITH_SAME_NAME(AudioSurroundModeNext)
-// DEFINE_KEYNAME_WITH_SAME_NAME(AVRInput)
-// DEFINE_KEYNAME_WITH_SAME_NAME(AVRPower)
+DEFINE_KEYNAME_WITH_SAME_NAME(AVRInput)
+DEFINE_KEYNAME_WITH_SAME_NAME(AVRPower)
 DEFINE_KEYNAME_WITH_SAME_NAME(ChannelDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(ChannelUp)
-DEFINE_KEYNAME_WITH_SAME_NAME(Red) // Rename to ColorF0Red
-DEFINE_KEYNAME_WITH_SAME_NAME(Green) // Rename to ColorF1Green
-DEFINE_KEYNAME_WITH_SAME_NAME(Yellow) // Rename to ColorF2Yellow
-DEFINE_KEYNAME_WITH_SAME_NAME(Blue) // Rename to ColorF3Blue
-// DEFINE_KEYNAME_WITH_SAME_NAME(Grey) // Rename to ColorF4Grey
-// DEFINE_KEYNAME_WITH_SAME_NAME(Brown) // Rename to ColorF5Brown
+DEFINE_KEYNAME_WITH_SAME_NAME(ColorF0Red)
+DEFINE_KEYNAME_WITH_SAME_NAME(ColorF1Green)
+DEFINE_KEYNAME_WITH_SAME_NAME(ColorF2Yellow)
+DEFINE_KEYNAME_WITH_SAME_NAME(ColorF3Blue)
+// DEFINE_KEYNAME_WITH_SAME_NAME(ColorF4Grey)
+// DEFINE_KEYNAME_WITH_SAME_NAME(ColorF5Brown)
 // DEFINE_KEYNAME_WITH_SAME_NAME(ClosedCaptionToggle)
 DEFINE_KEYNAME_WITH_SAME_NAME(Dimmer)
 // DEFINE_KEYNAME_WITH_SAME_NAME(DisplaySwap)
-DEFINE_KEYNAME_WITH_SAME_NAME(Exit)
-// DEFINE_KEYNAME_WITH_SAME_NAME(ClearFavorite0) // Rename to FavoriteClear0
-// DEFINE_KEYNAME_WITH_SAME_NAME(ClearFavorite1) // Rename to FavoriteClear1
-// DEFINE_KEYNAME_WITH_SAME_NAME(ClearFavorite2) // Rename to FavoriteClear2
-// DEFINE_KEYNAME_WITH_SAME_NAME(ClearFavorite3) // Rename to FavoriteClear3
-// DEFINE_KEYNAME_WITH_SAME_NAME(RecallFavorite0) // Rename to FavoriteRecall0
-// DEFINE_KEYNAME_WITH_SAME_NAME(RecallFavorite1) // Rename to FavoriteRecall1
-// DEFINE_KEYNAME_WITH_SAME_NAME(RecallFavorite2) // Rename to FavoriteRecall2
-// DEFINE_KEYNAME_WITH_SAME_NAME(RecallFavorite3) // Rename to FavoriteRecall3
-// DEFINE_KEYNAME_WITH_SAME_NAME(StoreFavorite0) // Rename to FavoriteStore0
-// DEFINE_KEYNAME_WITH_SAME_NAME(StoreFavorite1) // Rename to FavoriteStore1
-// DEFINE_KEYNAME_WITH_SAME_NAME(StoreFavorite2) // Rename to FavoriteStore2
-// DEFINE_KEYNAME_WITH_SAME_NAME(StoreFavorite3) // Rename to FavoriteStore3
+// DEFINE_KEYNAME_WITH_SAME_NAME(Exit)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteClear0)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteClear1)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteClear2)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteClear3)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteRecall0)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteRecall1)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteRecall2)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteRecall3)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteStore0)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteStore1)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteStore2)
+// DEFINE_KEYNAME_WITH_SAME_NAME(FavoriteStore3)
 DEFINE_KEYNAME_WITH_SAME_NAME(Guide)
-// DEFINE_KEYNAME_WITH_SAME_NAME(NextDay) // Rename to GuideNextDay
-// DEFINE_KEYNAME_WITH_SAME_NAME(PrevDay) // Rename to GuidePreviousDay
+// DEFINE_KEYNAME_WITH_SAME_NAME(GuideNextDay)
+// DEFINE_KEYNAME_WITH_SAME_NAME(GuidePreviousDay)
 DEFINE_KEYNAME_WITH_SAME_NAME(Info)
 // DEFINE_KEYNAME_WITH_SAME_NAME(InstantReplay)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Link)
-// DEFINE_KEYNAME_WITH_SAME_NAME(List) // Rename to ListProgram
-DEFINE_KEYNAME_WITH_SAME_NAME(Live) // Rename to LiveContent
+// DEFINE_KEYNAME_WITH_SAME_NAME(ListProgram)
+// DEFINE_KEYNAME_WITH_SAME_NAME(LiveContent)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Lock)
-DEFINE_KEYNAME_WITH_SAME_NAME(Apps) // Rename to MediaApps
-DEFINE_KEYNAME_WITH_SAME_NAME(FastFwd) // Rename to MediaFastForward
+// DEFINE_KEYNAME_WITH_SAME_NAME(MediaApps)
+DEFINE_KEYNAME_WITH_SAME_NAME(MediaFastForward)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaLast)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaPause)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaPlay)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaRecord)
 DEFINE_KEYNAME_WITH_SAME_NAME(MediaRewind)
 // DEFINE_KEYNAME_WITH_SAME_NAME(MediaSkip)
 // DEFINE_KEYNAME_WITH_SAME_NAME(NextFavoriteChannel)
 // DEFINE_KEYNAME_WITH_SAME_NAME(NextUserProfile)
@@ -335,41 +335,21 @@ DEFINE_KEYNAME_WITH_SAME_NAME(PinPToggle
 DEFINE_KEYNAME_WITH_SAME_NAME(RandomToggle)
 // DEFINE_KEYNAME_WITH_SAME_NAME(RcLowBattery)
 // DEFINE_KEYNAME_WITH_SAME_NAME(RecordSpeedNext)
 // DEFINE_KEYNAME_WITH_SAME_NAME(RfBypass)
 // DEFINE_KEYNAME_WITH_SAME_NAME(ScanChannelsToggle)
 // DEFINE_KEYNAME_WITH_SAME_NAME(ScreenModeNext)
 DEFINE_KEYNAME_WITH_SAME_NAME(Settings)
 // DEFINE_KEYNAME_WITH_SAME_NAME(SplitScreenToggle)
-// DEFINE_KEYNAME_WITH_SAME_NAME(STBInput)
-// DEFINE_KEYNAME_WITH_SAME_NAME(STBPower)
+DEFINE_KEYNAME_WITH_SAME_NAME(STBInput)
+DEFINE_KEYNAME_WITH_SAME_NAME(STBPower)
 DEFINE_KEYNAME_WITH_SAME_NAME(Subtitle)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Teletext)
-// DEFINE_KEYNAME_WITH_SAME_NAME(TV)
-// DEFINE_KEYNAME_WITH_SAME_NAME(TVInput)
-// DEFINE_KEYNAME_WITH_SAME_NAME(TVPower)
-// DEFINE_KEYNAME_WITH_SAME_NAME(VideoModeNext)
+DEFINE_KEYNAME_WITH_SAME_NAME(TV)
+DEFINE_KEYNAME_WITH_SAME_NAME(TVInput)
+DEFINE_KEYNAME_WITH_SAME_NAME(TVPower)
+DEFINE_KEYNAME_WITH_SAME_NAME(VideoModeNext)
 // DEFINE_KEYNAME_WITH_SAME_NAME(Wink)
-DEFINE_KEYNAME_WITH_SAME_NAME(Zoom) // Rename to ZoomToggle
-
-/******************************************************************************
- * Deprecated
- ******************************************************************************/
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadGrave)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadAcute)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadCircumflex)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadTilde)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadMacron)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadBreve)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadAboveDot)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadUmlaut)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadAboveRing)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadDoubleacute)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadCaron)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadCedilla)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadOgonek)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadIota)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadVoicedSound)
-DEFINE_KEYNAME_WITH_SAME_NAME(DeadSemivoicedSound)
+DEFINE_KEYNAME_WITH_SAME_NAME(ZoomToggle)
 
 #undef DEFINE_KEYNAME_WITH_SAME_NAME
 #undef DEFINE_KEYNAME_INTERNAL
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/KeyboardEvent.h"
 #include "mozilla/TextEvents.h"
-#include "nsIDocument.h"
 #include "prtime.h"
 
 namespace mozilla {
 namespace dom {
 
 KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
                              nsPresContext* aPresContext,
                              WidgetKeyboardEvent* aEvent)
@@ -120,120 +119,17 @@ KeyboardEvent::GetModifierState(const ns
 
   *aState = GetModifierState(aKey);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 KeyboardEvent::GetKey(nsAString& aKeyName)
 {
-  WidgetKeyboardEvent* keyboardEvent = mEvent->AsKeyboardEvent();
-  keyboardEvent->GetDOMKeyName(aKeyName);
-
-  nsIDocument::DeprecatedOperations deprecatedOperation;
-  switch (keyboardEvent->mKeyNameIndex) {
-    case KEY_NAME_INDEX_Down:
-      deprecatedOperation = nsIDocument::eKeyNameDown;
-      break;
-    case KEY_NAME_INDEX_Left:
-      deprecatedOperation = nsIDocument::eKeyNameLeft;
-      break;
-    case KEY_NAME_INDEX_Right:
-      deprecatedOperation = nsIDocument::eKeyNameRight;
-      break;
-    case KEY_NAME_INDEX_Up:
-      deprecatedOperation = nsIDocument::eKeyNameUp;
-      break;
-    case KEY_NAME_INDEX_Crsel:
-      deprecatedOperation = nsIDocument::eKeyNameCrsel;
-      break;
-    case KEY_NAME_INDEX_Del:
-      deprecatedOperation = nsIDocument::eKeyNameDel;
-      break;
-    case KEY_NAME_INDEX_Exsel:
-      deprecatedOperation = nsIDocument::eKeyNameExsel;
-      break;
-    case KEY_NAME_INDEX_Menu:
-      deprecatedOperation = nsIDocument::eKeyNameMenu;
-      break;
-    case KEY_NAME_INDEX_Esc:
-      deprecatedOperation = nsIDocument::eKeyNameEsc;
-      break;
-    case KEY_NAME_INDEX_Nonconvert:
-      deprecatedOperation = nsIDocument::eKeyNameNonconvert;
-      break;
-    case KEY_NAME_INDEX_HalfWidth:
-      deprecatedOperation = nsIDocument::eKeyNameHalfWidth;
-      break;
-    case KEY_NAME_INDEX_RomanCharacters:
-      deprecatedOperation = nsIDocument::eKeyNameRomanCharacters;
-      break;
-    case KEY_NAME_INDEX_FullWidth:
-      deprecatedOperation = nsIDocument::eKeyNameFullWidth;
-      break;
-    case KEY_NAME_INDEX_SelectMedia:
-      deprecatedOperation = nsIDocument::eKeyNameSelectMedia;
-      break;
-    case KEY_NAME_INDEX_MediaNextTrack:
-      deprecatedOperation = nsIDocument::eKeyNameMediaNextTrack;
-      break;
-    case KEY_NAME_INDEX_MediaPreviousTrack:
-      deprecatedOperation = nsIDocument::eKeyNameMediaPreviousTrack;
-      break;
-    case KEY_NAME_INDEX_Red:
-      deprecatedOperation = nsIDocument::eKeyNameRed;
-      break;
-    case KEY_NAME_INDEX_Green:
-      deprecatedOperation = nsIDocument::eKeyNameGreen;
-      break;
-    case KEY_NAME_INDEX_Yellow:
-      deprecatedOperation = nsIDocument::eKeyNameYellow;
-      break;
-    case KEY_NAME_INDEX_Blue:
-      deprecatedOperation = nsIDocument::eKeyNameBlue;
-      break;
-    case KEY_NAME_INDEX_Live:
-      deprecatedOperation = nsIDocument::eKeyNameLive;
-      break;
-    case KEY_NAME_INDEX_Apps:
-      deprecatedOperation = nsIDocument::eKeyNameApps;
-      break;
-    case KEY_NAME_INDEX_FastFwd:
-      deprecatedOperation = nsIDocument::eKeyNameFastFwd;
-      break;
-    case KEY_NAME_INDEX_Zoom:
-      deprecatedOperation = nsIDocument::eKeyNameZoom;
-      break;
-    case KEY_NAME_INDEX_DeadGrave:
-    case KEY_NAME_INDEX_DeadAcute:
-    case KEY_NAME_INDEX_DeadCircumflex:
-    case KEY_NAME_INDEX_DeadTilde:
-    case KEY_NAME_INDEX_DeadMacron:
-    case KEY_NAME_INDEX_DeadBreve:
-    case KEY_NAME_INDEX_DeadAboveDot:
-    case KEY_NAME_INDEX_DeadUmlaut:
-    case KEY_NAME_INDEX_DeadAboveRing:
-    case KEY_NAME_INDEX_DeadDoubleacute:
-    case KEY_NAME_INDEX_DeadCaron:
-    case KEY_NAME_INDEX_DeadCedilla:
-    case KEY_NAME_INDEX_DeadOgonek:
-    case KEY_NAME_INDEX_DeadIota:
-    case KEY_NAME_INDEX_DeadVoicedSound:
-    case KEY_NAME_INDEX_DeadSemivoicedSound:
-      deprecatedOperation = nsIDocument::eKeyNameDeadKeys;
-      break;
-    default:
-      return NS_OK;
-  }
-
-  nsIDocument* doc = mOwner ? mOwner->GetExtantDoc() : nullptr;
-  if (NS_WARN_IF(!doc)) {
-    return NS_OK;
-  }
-  doc->WarnOnceAbout(deprecatedOperation);
+  mEvent->AsKeyboardEvent()->GetDOMKeyName(aKeyName);
   return NS_OK;
 }
 
 void
 KeyboardEvent::GetCode(nsAString& aCodeName)
 {
   mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
 }
--- 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/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -152,64 +152,14 @@ Window_ContentWarning=window._content is
 SyncXMLHttpRequestWarning=Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/
 ImplicitMetaViewportTagFallback=No meta-viewport tag found. Please explicitly specify one to prevent unexpected behavioural changes in future versions. For more help https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
 # LOCALIZATION NOTE: Do not translate "DataContainerEvent" or "CustomEvent"
 DataContainerEventWarning=Use of DataContainerEvent is deprecated. Use CustomEvent instead.
 # LOCALIZATION NOTE: Do not translate "sendAsBinary" or "send(Blob data)"
 SendAsBinaryWarning=The non-standard sendAsBinary method is deprecated and will soon be removed. Use the standard send(Blob data) method instead.
 # LOCALIZATION NOTE: Do not translate "window.controllers"
 Window_ControllersWarning=window.controllers is deprecated. Do not use it for UA detection.
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Down" and "ArrowDown".
-KeyNameDownWarning=KeyboardEvent.key value "Down" is obsolete and will be renamed to "ArrowDown". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Left" and "ArrowLeft".
-KeyNameLeftWarning=KeyboardEvent.key value "Left" is obsolete and will be renamed to "ArrowLeft". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Right" and "ArrowRight".
-KeyNameRightWarning=KeyboardEvent.key value "Right" is obsolete and will be renamed to "ArrowRight". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Up" and "ArrowUp".
-KeyNameUpWarning=KeyboardEvent.key value "Up" is obsolete and will be renamed to "ArrowUp". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Crsel" and "CrSel".
-KeyNameCrselWarning=KeyboardEvent.key value "Crsel" is obsolete and will be renamed to "CrSel". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Del" and "Delete".
-KeyNameDelWarning=KeyboardEvent.key value "Del" is obsolete and will be renamed to "Delete". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Exsel" and "ExSel".
-KeyNameExselWarning=KeyboardEvent.key value "Exsel" is obsolete and will be renamed to "ExSel". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Menu" and "ContextMenu".
-KeyNameMenuWarning=KeyboardEvent.key value "Menu" is obsolete and will be renamed to "ContextMenu". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Esc" and "Escape".
-KeyNameEscWarning=KeyboardEvent.key value "Esc" is obsolete and will be renamed to "Escape". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Nonconvert" and "NonConvert".
-KeyNameNonconvertWarning=KeyboardEvent.key value "Nonconvert" is obsolete and will be renamed to "NonConvert". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "HalfWidth" and "Hankaku".
-KeyNameHalfWidthWarning=KeyboardEvent.key value "HalfWidth" is obsolete and will be renamed to "Hankaku". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "RomanCharacters", "Romaji" and "Eisu".
-KeyNameRomanCharactersWarning=KeyboardEvent.key value "RomanCharacters" is obsolete and will be renamed to "Romaji" or remapped to "Eisu". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "FullWith" and "Zenkaku".
-KeyNameFullWidthWarning=KeyboardEvent.key value "FullWidth" is obsolete and will be renamed to "Zenkaku". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "SelectMedia" and "MediaSelect".
-KeyNameSelectMediaWarning=KeyboardEvent.key value "SelectMedia" is obsolete and will be renamed to "MediaSelect". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "MediaNextTrack" and "MediaTrackNext".
-KeyNameMediaNextTrackWarning=KeyboardEvent.key value "MediaNextTrack" is obsolete and will be renamed to "MediaTrackNext". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "MediaPreviousTrack" and "MediaTrackPrevious".
-KeyNameMediaPreviousTrackWarning=KeyboardEvent.key value "MediaPreviousTrack" is obsolete and will be renamed to "MediaTrackPrevious". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Red" and "ColorF0Red".
-KeyNameRedWarning=KeyboardEvent.key value "Red" is obsolete and will be renamed to "ColorF0Red". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Green" and "ColorF1Green".
-KeyNameGreenWarning=KeyboardEvent.key value "Green" is obsolete and will be renamed to "ColorF1Green". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Yellow" and "ColorF2Yellow".
-KeyNameYellowWarning=KeyboardEvent.key value "Yellow" is obsolete and will be renamed to "ColorF2Yellow". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Blue" and "ColorF3Blue".
-KeyNameBlueWarning=KeyboardEvent.key value "Blue" is obsolete and will be renamed to "ColorF3Blue". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Live".
-KeyNameLiveWarning=KeyboardEvent.key value "Live" is obsolete and will be removed. For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Apps".
-KeyNameAppsWarning=KeyboardEvent.key value "Apps" is obsolete and will be removed. For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "FastFwd" and "MediaFastForward".
-KeyNameFastFwdWarning=KeyboardEvent.key value "FastFwd" is obsolete and will be renamed to "MediaFastForward". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Zoom" and "ZoomToggle".
-KeyNameZoomWarning=KeyboardEvent.key value "Zoom" is obsolete and will be renamed to "ZoomToggle". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
-# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key" and "Dead".
-KeyNameDeadKeysWarning=KeyboardEvent.key values starting with "Dead" are obsolete and will be merged into just "Dead". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
 XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
 # LOCALIZATION NOTE: Do not translate "IndexedDB".
 IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
 # LOCALIZATION NOTE (WillChangeBudgetWarning): Do not translate Will-change, %1$S,%2$S,%3$S are numbers.
 WillChangeBudgetWarning=Will-change memory consumption is too high. Surface area covers %1$S pixels, budget is the document surface area multiplied by %2$S (%3$S pixels). All occurences of will-change in the document are ignored when over budget.
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -10,17 +10,18 @@ CrossSiteRequestBlocked=Cross-Origin Req
 InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header.
 # LOCALIZATION NOTE: Do not translate "Public-Key-Pins or HPKP"
 InvalidPKPHeaders=The site specified an invalid Public-Key-Pins header.
 # LOCALIZATION NOTE: Do not translate "SHA-1"
 SHA1Sig=This site makes use of a SHA-1 Certificate; it's recommended you use certificates with signature algorithms that use hash functions stronger than SHA-1.
 InsecurePasswordsPresentOnPage=Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen.
 InsecureFormActionPasswordsPresent=Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen.
 InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen.
-LoadingMixedActiveContent=Loading mixed (insecure) active content on a secure page "%1$S"
-LoadingMixedDisplayContent=Loading mixed (insecure) display content on a secure page "%1$S"
+# LOCALIZATION NOTE: "%1$S" is the URI of the insecure mixed content resource
+LoadingMixedActiveContent2=Loading mixed (insecure) active content "%1$S" on a secure page
+LoadingMixedDisplayContent2=Loading mixed (insecure) display content "%1$S" on a secure page
 # LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
 BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
 
 # LOCALIZATION NOTE: Do not translate "SSL 3.0".
 WeakProtocolVersionWarning=This site uses the protocol SSL 3.0 for encryption, which is deprecated and insecure.
 # LOCALIZATION NOTE: Do not translate "RC4".
 WeakCipherSuiteWarning=This site uses the cipher RC4 for encryption, which is deprecated and insecure.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -227,19 +227,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   mAmpleVideoFrames =
     std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
 
   mBufferingWait = mScheduler->IsRealTime() ? 0 : 30;
   mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS;
 
-  mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2;
-  mAudioPrerollUsecs = mScheduler->IsRealTime() ? 0 : LOW_AUDIO_USECS * 2;
-
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine thread isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
   // function per-process is OK, provided each call is matched by a corresponding
   // timeEndPeriod() call.
   timeBeginPeriod(1);
 #endif
@@ -628,28 +625,17 @@ MediaDecoderStateMachine::DecodeVideo()
     if (mState != DECODER_STATE_DECODING &&
         mState != DECODER_STATE_BUFFERING &&
         mState != DECODER_STATE_SEEKING) {
       mVideoRequestStatus = RequestStatus::Idle;
       DispatchDecodeTasksIfNeeded();
       return;
     }
 
-    // We don't want to consider skipping to the next keyframe if we've
-    // only just started up the decode loop, so wait until we've decoded
-    // some frames before enabling the keyframe skip logic on video.
-    if (mIsVideoPrerolling &&
-        (static_cast<uint32_t>(VideoQueue().GetSize())
-          >= mVideoPrerollFrames * mPlaybackRate))
-    {
-      mIsVideoPrerolling = false;
-    }
-
     skipToNextKeyFrame = NeedToSkipToNextKeyframe();
-
     currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
 
     // Time the video decode, so that if it's slow, we can increase our low
     // audio threshold to reduce the chance of an audio underrun while we're
     // waiting for a video decode to complete.
     mVideoDecodeStartTime = TimeStamp::Now();
   }
 
@@ -688,24 +674,16 @@ MediaDecoderStateMachine::DecodeAudio()
     if (mState != DECODER_STATE_DECODING &&
         mState != DECODER_STATE_BUFFERING &&
         mState != DECODER_STATE_SEEKING) {
       mAudioRequestStatus = RequestStatus::Idle;
       DispatchDecodeTasksIfNeeded();
       mon.NotifyAll();
       return;
     }
-
-    // We don't want to consider skipping to the next keyframe if we've
-    // only just started up the decode loop, so wait until we've decoded
-    // some audio data before enabling the keyframe skip logic on audio.
-    if (mIsAudioPrerolling &&
-        GetDecodedAudioDuration() >= mAudioPrerollUsecs * mPlaybackRate) {
-      mIsAudioPrerolling = false;
-    }
   }
 
   SAMPLE_LOG("DecodeAudio() queued=%i, decoder-queued=%o",
              AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
 
   mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
                                     &MediaDecoderStateMachine::OnAudioDecoded,
                                     &MediaDecoderStateMachine::OnAudioNotDecoded);
@@ -753,22 +731,29 @@ MediaDecoderStateMachine::OnAudioDecoded
 
   switch (mState) {
     case DECODER_STATE_DECODING_FIRSTFRAME: {
       Push(audio);
       MaybeFinishDecodeFirstFrame();
       return;
     }
 
-    case DECODER_STATE_BUFFERING:
+    case DECODER_STATE_BUFFERING: {
       // If we're buffering, this may be the sample we need to stop buffering.
-      // Schedule the state machine and then fall through.
+      // Save it and schedule the state machine.
+      Push(audio);
       ScheduleStateMachine();
+      return;
+    }
+
     case DECODER_STATE_DECODING: {
       Push(audio);
+      if (mIsAudioPrerolling && DonePrerollingAudio()) {
+        StopPrerollingAudio();
+      }
       return;
     }
 
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeekTarget.IsValid()) {
         // We've received a sample from a previous decode. Discard it.
         return;
       }
@@ -889,17 +874,23 @@ MediaDecoderStateMachine::OnNotDecoded(M
       mCurrentSeekTarget.IsValid() && mFirstVideoFrameAfterSeek) {
     // Null sample. Hit end of stream. If we have decoded a frame,
     // insert it into the queue so that we have something to display.
     // We make sure to do this before invoking VideoQueue().Finish()
     // below.
     VideoQueue().Push(mFirstVideoFrameAfterSeek);
     mFirstVideoFrameAfterSeek = nullptr;
   }
-  isAudio ? AudioQueue().Finish() : VideoQueue().Finish();
+  if (isAudio) {
+    AudioQueue().Finish();
+    StopPrerollingAudio();
+  } else {
+    VideoQueue().Finish();
+    StopPrerollingVideo();
+  }
   switch (mState) {
     case DECODER_STATE_DECODING_FIRSTFRAME: {
       MaybeFinishDecodeFirstFrame();
       return;
     }
 
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
@@ -968,22 +959,30 @@ MediaDecoderStateMachine::OnVideoDecoded
 
   switch (mState) {
     case DECODER_STATE_DECODING_FIRSTFRAME: {
       Push(video);
       MaybeFinishDecodeFirstFrame();
       return;
     }
 
-    case DECODER_STATE_BUFFERING:
+    case DECODER_STATE_BUFFERING: {
       // If we're buffering, this may be the sample we need to stop buffering.
-      // Schedule the state machine and then fall through.
+      // Save it and schedule the state machine.
+      Push(video);
       ScheduleStateMachine();
+      return;
+    }
+
     case DECODER_STATE_DECODING: {
       Push(video);
+      if (mIsVideoPrerolling && DonePrerollingVideo()) {
+        StopPrerollingVideo();
+      }
+
       // If the requested video sample was slow to arrive, increase the
       // amount of audio we buffer to ensure that we don't run out of audio.
       // TODO: Detect when we're truly async, and don't do this if so, as
       // it's not necessary.
       TimeDuration decodeTime = TimeStamp::Now() - mVideoDecodeStartTime;
       if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
           !HasLowUndecodedData())
       {
@@ -1192,32 +1191,44 @@ int64_t MediaDecoderStateMachine::GetCur
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(mSyncPointInDecodedStream >= 0, "Should have set up sync point");
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
   int64_t streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream;
   return mSyncPointInDecodedStream + streamDelta;
 }
 
-void MediaDecoderStateMachine::StartPlayback()
+void MediaDecoderStateMachine::MaybeStartPlayback()
 {
-  DECODER_LOG("StartPlayback()");
-
-  NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
   AssertCurrentThreadInMonitor();
+  if (IsPlaying()) {
+    // Logging this case is really spammy - don't do it.
+    return;
+  }
+
+  bool playStatePermits = mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING;
+  bool decodeStatePermits = mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED;
+  if (!playStatePermits || !decodeStatePermits || mIsAudioPrerolling || mIsVideoPrerolling) {
+    DECODER_LOG("Not starting playback [playStatePermits: %d, decodeStatePermits: %d, "
+                "mIsAudioPrerolling: %d, mIsVideoPrerolling: %d]", (int) playStatePermits,
+                (int) decodeStatePermits, (int) mIsAudioPrerolling, (int) mIsVideoPrerolling);
+    return;
+  }
 
   if (mDecoder->CheckDecoderCanOffloadAudio()) {
     DECODER_LOG("Offloading playback");
     return;
   }
 
+  DECODER_LOG("MaybeStartPlayback() starting playback");
+
   mDecoder->NotifyPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
-
-  NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
+  MOZ_ASSERT(IsPlaying());
+
   nsresult rv = StartAudioThread();
   NS_ENSURE_SUCCESS_VOID(rv);
 
   mDecoder->GetReentrantMonitor().NotifyAll();
   mDecoder->UpdateStreamBlockingForStateMachinePlaying();
   DispatchDecodeTasksIfNeeded();
 }
 
@@ -1455,18 +1466,18 @@ void MediaDecoderStateMachine::StartDeco
   mDecodeStartTime = TimeStamp::Now();
 
   CheckIfDecodeComplete();
   if (mState == DECODER_STATE_COMPLETED) {
     return;
   }
 
   // Reset other state to pristine values before starting decode.
-  mIsAudioPrerolling = true;
-  mIsVideoPrerolling = true;
+  mIsAudioPrerolling = !DonePrerollingAudio();
+  mIsVideoPrerolling = !DonePrerollingVideo();
 
   // Ensure that we've got tasks enqueued to decode data if we need to.
   DispatchDecodeTasksIfNeeded();
 
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::StartWaitForResources()
@@ -1497,35 +1508,55 @@ void MediaDecoderStateMachine::DoNotifyW
   }
   DECODER_LOG("DoNotifyWaitingForResourcesStatusChanged");
   // The reader is no longer waiting for resources (say a hardware decoder),
   // we can now proceed to decode metadata.
   SetState(DECODER_STATE_DECODING_NONE);
   ScheduleStateMachine();
 }
 
-void MediaDecoderStateMachine::Play()
+void MediaDecoderStateMachine::PlayInternal()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+  // Once we start playing, we don't want to minimize our prerolling, as we
+  // assume the user is likely to want to keep playing in future. This needs to
+  // happen before we invoke StartDecoding().
+  if (mMinimizePreroll) {
+    mMinimizePreroll = false;
+    DispatchDecodeTasksIfNeeded();
+  }
+
+  // Some state transitions still happen synchronously on the main thread. So
+  // if the main thread invokes Play() and then Seek(), the seek will initiate
+  // synchronously on the main thread, and the asynchronous PlayInternal task
+  // will arrive when it's no longer valid. The proper thing to do is to move
+  // all state transitions to the state machine thread, but for now we just
+  // make sure that none of the possible main-thread state transitions (Seek(),
+  // SetDormant(), and Shutdown()) have not occurred.
+  if (mState != DECODER_STATE_DECODING && mState != DECODER_STATE_BUFFERING &&
+      mState != DECODER_STATE_COMPLETED)
+  {
+    DECODER_LOG("Unexpected state - Bailing out of PlayInternal()");
+    return;
+  }
+
   // When asked to play, switch to decoding state only if
   // we are currently buffering. In other cases, we'll start playing anyway
   // when the state machine notices the decoder's state change to PLAYING.
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState == DECODER_STATE_BUFFERING) {
-    DECODER_LOG("Changed state from BUFFERING to DECODING");
-    SetState(DECODER_STATE_DECODING);
-    mDecodeStartTime = TimeStamp::Now();
+    StartDecoding();
   }
+
   if (mDecodingFrozenAtStateDecoding) {
     mDecodingFrozenAtStateDecoding = false;
     DispatchDecodeTasksIfNeeded();
   }
-  // Once we start playing, we don't want to minimize our prerolling, as we
-  // assume the user is likely to want to keep playing in future.
-  mMinimizePreroll = false;
+
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::ResetPlayback()
 {
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING ||
              mState == DECODER_STATE_SHUTDOWN ||
@@ -2254,23 +2285,17 @@ MediaDecoderStateMachine::FinishDecodeFi
   if (mState == DECODER_STATE_DECODING_FIRSTFRAME) {
     StartDecoding();
   }
 
   // For very short media the first frame decode can decode the entire media.
   // So we need to check if this has occurred, else our decode pipeline won't
   // run (since it doesn't need to) and we won't detect end of stream.
   CheckIfDecodeComplete();
-
-  if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) &&
-      mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
-      !IsPlaying())
-  {
-    StartPlayback();
-  }
+  MaybeStartPlayback();
 
   if (mQueuedSeekTarget.IsValid()) {
     EnqueueStartQueuedSeekTask();
   }
 
   return NS_OK;
 }
 
@@ -2637,22 +2662,18 @@ nsresult MediaDecoderStateMachine::RunSt
       if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING &&
           IsPlaying())
       {
         // We're playing, but the element/decoder is in paused state. Stop
         // playing!
         StopPlayback();
       }
 
-      if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
-          !IsPlaying()) {
-        // We are playing, but the state machine does not know it yet. Tell it
-        // that it is, so that the clock can be properly queried.
-        StartPlayback();
-      }
+      // Start playback if necessary so that the clock can be properly queried.
+      MaybeStartPlayback();
 
       AdvanceFrame();
       NS_ASSERTION(mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING ||
                    IsStateMachineScheduled() ||
                    mPlaybackRate == 0.0, "Must have timer scheduled");
       return NS_OK;
     }
 
@@ -2677,37 +2698,33 @@ nsresult MediaDecoderStateMachine::RunSt
                       (mQuickBuffering ? "(quick exit)" : ""));
           ScheduleStateMachine(USECS_PER_S);
           return NS_OK;
         }
       } else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
         MOZ_ASSERT(mReader->IsWaitForDataSupported(),
                    "Don't yet have a strategy for non-heuristic + non-WaitForData");
         DispatchDecodeTasksIfNeeded();
-        MOZ_ASSERT_IF(OutOfDecodedAudio(), mAudioRequestStatus != RequestStatus::Idle);
-        MOZ_ASSERT_IF(OutOfDecodedVideo(), mVideoRequestStatus != RequestStatus::Idle);
+        MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedAudio(), mAudioRequestStatus != RequestStatus::Idle);
+        MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedVideo(), mVideoRequestStatus != RequestStatus::Idle);
         DECODER_LOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
                     "mAudioStatus: %d, outOfVideo: %d, mVideoStatus: %d",
                     OutOfDecodedAudio(), mAudioRequestStatus,
                     OutOfDecodedVideo(), mVideoRequestStatus);
         return NS_OK;
       }
 
       DECODER_LOG("Changed state from BUFFERING to DECODING");
       DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
       StartDecoding();
 
       // Notify to allow blocked decoder thread to continue
       mDecoder->GetReentrantMonitor().NotifyAll();
       UpdateReadyState();
-      if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
-          !IsPlaying())
-      {
-        StartPlayback();
-      }
+      MaybeStartPlayback();
       NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_SEEKING: {
       return EnqueueDecodeSeekTask();
     }
 
@@ -2971,18 +2988,18 @@ void MediaDecoderStateMachine::AdvanceFr
       // decoding and quick-buffering.
       ScheduleStateMachine(USECS_PER_S);
       return;
     }
   }
 
   // We've got enough data to keep playing until at least the next frame.
   // Start playing now if need be.
-  if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) {
-    StartPlayback();
+  if ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0) {
+    MaybeStartPlayback();
   }
 
   if (currentFrame) {
     // Decode one frame and display it.
     int64_t delta = currentFrame->mTime - clock_time;
     TimeStamp presTime = nowTime + TimeDuration::FromMicroseconds(delta / mPlaybackRate);
     NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
     // Filter out invalid frames by checking the frame time. FrameTime could be
@@ -3358,16 +3375,17 @@ void MediaDecoderStateMachine::SetPreser
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
 }
 
 void
 MediaDecoderStateMachine::SetMinimizePrerollUntilPlaybackStarts()
 {
   AssertCurrentThreadInMonitor();
+  DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
   mMinimizePreroll = true;
 }
 
 bool MediaDecoderStateMachine::IsShutdown()
 {
   AssertCurrentThreadInMonitor();
   return GetState() == DECODER_STATE_SHUTDOWN;
 }
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -84,16 +84,17 @@ hardware (via AudioStream).
 
 #include "mozilla/Attributes.h"
 #include "nsThreadUtils.h"
 #include "MediaDecoder.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaMetadataManager.h"
+#include "MediaDecoderStateMachineScheduler.h"
 
 class nsITimer;
 
 namespace mozilla {
 
 class AudioSegment;
 class VideoSegment;
 class MediaTaskQueue;
@@ -192,17 +193,28 @@ public:
   bool OnDecodeThread() const;
   bool OnStateMachineThread() const;
 
   MediaDecoderOwner::NextFrameStatus GetNextFrameStatus();
 
   // Cause state transitions. These methods obtain the decoder monitor
   // to synchronise the change of state, and to notify other threads
   // that the state has changed.
-  void Play();
+  void Play()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsRefPtr<nsRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::PlayInternal);
+    GetStateMachineThread()->Dispatch(r, NS_DISPATCH_NORMAL);
+  }
+
+private:
+  // The actual work for the above, which happens asynchronously on the state
+  // machine thread.
+  void PlayInternal();
+public:
 
   // Seeks to the decoder to aTarget asynchronously.
   // Must be called from the main thread.
   void Seek(const SeekTarget& aTarget);
 
   // Dispatches a task to the main thread to seek to mQueuedSeekTarget.
   // This is threadsafe and can be called on any thread.
   void EnqueueStartQueuedSeekTask();
@@ -575,19 +587,20 @@ protected:
   // Starts the audio thread. The decoder monitor must be held with exactly
   // one lock count. Called on the state machine thread.
   nsresult StartAudioThread();
 
   // Sets internal state which causes playback of media to pause.
   // The decoder monitor must be held.
   void StopPlayback();
 
-  // Sets internal state which causes playback of media to begin or resume.
+  // If the conditions are right, sets internal state which causes playback
+  // of media to begin or resume.
   // Must be called with the decode monitor held.
-  void StartPlayback();
+  void MaybeStartPlayback();
 
   // Moves the decoder into decoding state. Called on the state machine
   // thread. The decoder monitor must be held.
   void StartDecoding();
 
   // Moves the decoder into the shutdown state, and dispatches an error
   // event to the media element. This begins shutting down the decoder.
   // The decoder monitor must be held. This is only called on the
@@ -926,18 +939,58 @@ protected:
   int64_t mAmpleAudioThresholdUsecs;
 
   // At the start of decoding we want to "preroll" the decode until we've
   // got a few frames decoded before we consider whether decode is falling
   // behind. Otherwise our "we're falling behind" logic will trigger
   // unneccessarily if we start playing as soon as the first sample is
   // decoded. These two fields store how many video frames and audio
   // samples we must consume before are considered to be finished prerolling.
-  uint32_t mAudioPrerollUsecs;
-  uint32_t mVideoPrerollFrames;
+  uint32_t AudioPrerollUsecs() const
+  {
+    if (mScheduler->IsRealTime()) {
+      return 0;
+    }
+
+    uint32_t result = mLowAudioThresholdUsecs * 2;
+    MOZ_ASSERT(result <= mAmpleAudioThresholdUsecs, "Prerolling will never finish");
+    return result;
+  }
+  uint32_t VideoPrerollFrames() const { return mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2; }
+
+  bool DonePrerollingAudio()
+  {
+    AssertCurrentThreadInMonitor();
+    return !IsAudioDecoding() || GetDecodedAudioDuration() >= AudioPrerollUsecs() * mPlaybackRate;
+  }
+
+  bool DonePrerollingVideo()
+  {
+    AssertCurrentThreadInMonitor();
+    return !IsVideoDecoding() ||
+           static_cast<uint32_t>(VideoQueue().GetSize()) >= VideoPrerollFrames() * mPlaybackRate;
+  }
+
+  void StopPrerollingAudio()
+  {
+    AssertCurrentThreadInMonitor();
+    if (mIsAudioPrerolling) {
+      mIsAudioPrerolling = false;
+      ScheduleStateMachine();
+    }
+  }
+
+  void StopPrerollingVideo()
+  {
+    AssertCurrentThreadInMonitor();
+    if (mIsVideoPrerolling) {
+      mIsVideoPrerolling = false;
+      ScheduleStateMachine();
+    }
+  }
 
   // This temporarily stores the first frame we decode after we seek.
   // This is so that if we hit end of stream while we're decoding to reach
   // the seek target, we will still have a frame that we can display as the
   // last frame in the media.
   nsRefPtr<VideoData> mFirstVideoFrameAfterSeek;
 
   // When we start decoding (either for the first time, or after a pause)
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -67,17 +67,17 @@ MP4Reader::MP4Reader(AbstractMediaDecode
   : MediaDecoderReader(aDecoder)
   , mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
   , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mDemuxerInitialized(false)
   , mIsEncrypted(false)
   , mIndexReady(false)
-  , mIndexMonitor("MP4 index")
+  , mDemuxerMonitor("MP4 Demuxer")
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
 {
   MOZ_COUNT_DTOR(MP4Reader);
@@ -152,17 +152,17 @@ MP4Reader::InitLayersBackendType()
 
 static bool sIsEMEEnabled = false;
 
 nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   PlatformDecoderModule::Init();
-  mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource(), &mIndexMonitor), &mIndexMonitor);
+  mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource(), &mDemuxerMonitor), &mDemuxerMonitor);
 
   InitLayersBackendType();
 
   mAudio.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
 
   mVideo.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
@@ -289,30 +289,29 @@ MP4Reader::PreReadMetadata()
   }
 }
 
 nsresult
 MP4Reader::ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags)
 {
   if (!mDemuxerInitialized) {
-    {
-      MonitorAutoLock mon(mIndexMonitor);
-      bool ok = mDemuxer->Init();
-      NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
-      mIndexReady = true;
-    }
+    MonitorAutoLock mon(mDemuxerMonitor);
+    bool ok = mDemuxer->Init();
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+    mIndexReady = true;
 
     // To decode, we need valid video and a place to put it.
     mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo() &&
                                               mDecoder->GetImageContainer();
 
     mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
 
     {
+      MonitorAutoUnlock unlock(mDemuxerMonitor);
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       mIsEncrypted = mDemuxer->Crypto().valid;
     }
 
     // Remember that we've initialized the demuxer, so that if we're decoding
     // an encrypted stream and we need to wait for a CDM to be set, we don't
     // need to reinit the demuxer.
     mDemuxerInitialized = true;
@@ -406,42 +405,47 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
                                                       mVideo.mCallback);
     }
     NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE);
     nsresult rv = mVideo.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Get the duration, and report it to the decoder if we have it.
-  Microseconds duration = mDemuxer->Duration();
+  Microseconds duration;
+  {
+    MonitorAutoLock lock(mDemuxerMonitor);
+    duration = mDemuxer->Duration();
+  }
   if (duration != -1) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(duration);
   }
 
   *aInfo = mInfo;
   *aTags = nullptr;
 
-  MonitorAutoLock mon(mIndexMonitor);
+  MonitorAutoLock mon(mDemuxerMonitor);
   UpdateIndex();
 
   return NS_OK;
 }
 
 void
 MP4Reader::ReadUpdatedMetadata(MediaInfo* aInfo)
 {
   *aInfo = mInfo;
 }
 
 bool
 MP4Reader::IsMediaSeekable()
 {
   // We can seek if we get a duration *and* the reader reports that it's
   // seekable.
+  MonitorAutoLock mon(mDemuxerMonitor);
   return mDecoder->GetResource()->IsTransportSeekable() && mDemuxer->CanSeek();
 }
 
 bool
 MP4Reader::HasAudio()
 {
   return mAudio.mActive;
 }
@@ -626,17 +630,24 @@ MP4Reader::ReturnOutput(MediaData* aData
   } else if (aTrack == kVideo) {
     mVideo.mPromise.Resolve(static_cast<VideoData*>(aData), __func__);
   }
 }
 
 MP4Sample*
 MP4Reader::PopSample(TrackType aTrack)
 {
-  MonitorAutoLock mon(mIndexMonitor);
+  MonitorAutoLock mon(mDemuxerMonitor);
+  return PopSampleLocked(aTrack);
+}
+
+MP4Sample*
+MP4Reader::PopSampleLocked(TrackType aTrack)
+{
+  mDemuxerMonitor.AssertCurrentThreadOwns();
   switch (aTrack) {
     case kAudio:
       return mDemuxer->DemuxAudioSample();
 
     case kVideo:
       if (mQueuedVideoSample) {
         return mQueuedVideoSample.forget();
       }
@@ -668,22 +679,22 @@ MP4Reader::SizeOfQueue(TrackType aTrack)
 }
 
 nsresult
 MP4Reader::ResetDecode()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   Flush(kVideo);
   {
-    MonitorAutoLock mon(mIndexMonitor);
+    MonitorAutoLock mon(mDemuxerMonitor);
     mDemuxer->SeekVideo(0);
   }
   Flush(kAudio);
   {
-    MonitorAutoLock mon(mIndexMonitor);
+    MonitorAutoLock mon(mDemuxerMonitor);
     mDemuxer->SeekAudio(0);
   }
   return MediaDecoderReader::ResetDecode();
 }
 
 void
 MP4Reader::Output(TrackType aTrack, MediaData* aSample)
 {
@@ -816,25 +827,26 @@ MP4Reader::SkipVideoDemuxToNextKeyFrame(
 nsRefPtr<MediaDecoderReader::SeekPromise>
 MP4Reader::Seek(int64_t aTime,
                 int64_t aStartTime,
                 int64_t aEndTime,
                 int64_t aCurrentTime)
 {
   LOG("MP4Reader::Seek(%lld)", aTime);
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
+  MonitorAutoLock mon(mDemuxerMonitor);
   if (!mDecoder->GetResource()->IsTransportSeekable() || !mDemuxer->CanSeek()) {
     VLOG("Seek() END (Unseekable)");
     return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   mQueuedVideoSample = nullptr;
   if (mDemuxer->HasValidVideo()) {
     mDemuxer->SeekVideo(aTime);
-    mQueuedVideoSample = PopSample(kVideo);
+    mQueuedVideoSample = PopSampleLocked(kVideo);
   }
   if (mDemuxer->HasValidAudio()) {
     mDemuxer->SeekAudio(
       mQueuedVideoSample ? mQueuedVideoSample->composition_timestamp : aTime);
   }
   LOG("MP4Reader::Seek(%lld) exit", aTime);
   return SeekPromise::CreateAndResolve(true, __func__);
 }
@@ -851,28 +863,28 @@ MP4Reader::UpdateIndex()
   if (NS_SUCCEEDED(resource->GetCachedRanges(ranges))) {
     mDemuxer->UpdateIndex(ranges);
   }
 }
 
 int64_t
 MP4Reader::GetEvictionOffset(double aTime)
 {
-  MonitorAutoLock mon(mIndexMonitor);
+  MonitorAutoLock mon(mDemuxerMonitor);
   if (!mIndexReady) {
     return 0;
   }
 
   return mDemuxer->GetEvictionOffset(aTime * 1000000.0);
 }
 
 nsresult
 MP4Reader::GetBuffered(dom::TimeRanges* aBuffered)
 {
-  MonitorAutoLock mon(mIndexMonitor);
+  MonitorAutoLock mon(mDemuxerMonitor);
   if (!mIndexReady) {
     return NS_OK;
   }
   UpdateIndex();
   MOZ_ASSERT(mStartTime != -1, "Need to finish metadata decode first");
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> ranges;
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -97,16 +97,17 @@ private:
   void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
 
   // Initializes mLayersBackendType if possible.
   void InitLayersBackendType();
 
   // Blocks until the demuxer produces an sample of specified type.
   // Returns nullptr on error on EOS. Caller must delete sample.
   mp4_demuxer::MP4Sample* PopSample(mp4_demuxer::TrackType aTrack);
+  mp4_demuxer::MP4Sample* PopSampleLocked(mp4_demuxer::TrackType aTrack);
 
   bool SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed);
 
   // DecoderCallback proxies the MediaDataDecoderCallback calls to these
   // functions.
   void Output(mp4_demuxer::TrackType aType, MediaData* aSample);
   void InputExhausted(mp4_demuxer::TrackType aTrack);
   void Error(mp4_demuxer::TrackType aTrack);
@@ -254,15 +255,15 @@ private:
 
   // True if we've read the streams' metadata.
   bool mDemuxerInitialized;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 
   bool mIndexReady;
-  Monitor mIndexMonitor;
+  Monitor mDemuxerMonitor;
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/apple/AppleVDADecoder.cpp
+++ b/dom/media/fmp4/apple/AppleVDADecoder.cpp
@@ -284,20 +284,18 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
     NS_ERROR("Couldn't create VideoData for frame");
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
   // Frames come out in DTS order but we need to output them
   // in composition order.
   mReorderQueue.Push(data);
-  // Assume a frame with a PTS <= current DTS is ready.
   while (mReorderQueue.Length() > mMaxRefFrames) {
-    nsRefPtr<VideoData> readyData = mReorderQueue.Pop();
-      mCallback->Output(readyData);
+    mCallback->Output(mReorderQueue.Pop());
   }
   LOG("%llu decoded frames queued",
       static_cast<unsigned long long>(mReorderQueue.Length()));
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -97,16 +97,17 @@ EXPORTS += [
     'GraphDriver.h',
     'Latency.h',
     'MediaCache.h',
     'MediaData.h',
     'MediaDecoder.h',
     'MediaDecoderOwner.h',
     'MediaDecoderReader.h',
     'MediaDecoderStateMachine.h',
+    'MediaDecoderStateMachineScheduler.h',
     'MediaInfo.h',
     'MediaMetadataManager.h',
     'MediaPromise.h',
     'MediaQueue.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaSegment.h',
     'MediaStreamGraph.h',
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -32,16 +32,17 @@
 
 #include "MediaStreamSource.h"
 #include "MediaTaskQueue.h"
 #include "MP3FrameParser.h"
 #include "nsThreadUtils.h"
 #include "ImageContainer.h"
 #include "SharedThreadPool.h"
 #include "VideoFrameContainer.h"
+#include "VideoUtils.h"
 
 using namespace android;
 
 namespace mozilla {
 
 enum {
   kNotifyCodecReserved = 'core',
   kNotifyCodecCanceled = 'coca',
@@ -1270,24 +1271,22 @@ MediaCodecReader::ShutdownTaskQueues()
   }
 }
 
 bool
 MediaCodecReader::CreateTaskQueues()
 {
   if (mAudioTrack.mSource != nullptr && mAudioTrack.mCodec != nullptr &&
       !mAudioTrack.mTaskQueue) {
-    mAudioTrack.mTaskQueue = new MediaTaskQueue(
-      SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaCodecReader Audio"), 1));
+    mAudioTrack.mTaskQueue = CreateMediaDecodeTaskQueue();
     NS_ENSURE_TRUE(mAudioTrack.mTaskQueue, false);
   }
- if (mVideoTrack.mSource != nullptr && mVideoTrack.mCodec != nullptr &&
-     !mVideoTrack.mTaskQueue) {
-    mVideoTrack.mTaskQueue = new MediaTaskQueue(
-      SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaCodecReader Video"), 1));
+  if (mVideoTrack.mSource != nullptr && mVideoTrack.mCodec != nullptr &&
+      !mVideoTrack.mTaskQueue) {
+    mVideoTrack.mTaskQueue = CreateMediaDecodeTaskQueue();
     NS_ENSURE_TRUE(mVideoTrack.mTaskQueue, false);
   }
 
   return true;
 }
 
 bool
 MediaCodecReader::CreateMediaCodecs()
--- a/dom/media/test/test_bug448534.html
+++ b/dom/media/test/test_bug448534.html
@@ -14,36 +14,39 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448535">Mozilla Bug 448534</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var manager = new MediaTestManager;
 
 function loaded(event) {
   var v = event.target;
+  info(v.token + ": event=" + event.type);
   if (v._finished)
     return;
   v.play();
 }
 
 function started(event) {
   var v = event.target;
+  info(v.token + ": event=" + event.type);
   if (v._finished)
     return;
-  ok(!v.paused, "Video should not be paused while playing");
+  ok(!v.paused, v.token + ": Video should not be paused while playing");
   v.parentNode.removeChild(v);
   v._played = true;
 }
 
 function stopped(event) {
   var v = event.target;
+  info(v.token + ": event=" + event.type);
   if (v._finished)
     return;
   v._finished = true;
-  ok(v.paused, "Video should be paused after removing from the Document");
+  ok(v.paused, v.token + ": Video should be paused after removing from the Document");
   removeNodeAndSource(v);
   manager.finished(v.token);
 }
 
 
 function startTest(test, token) {
   var v = document.createElement('video');
   v.preload = "metadata";
--- a/dom/nfc/NfcContentHelper.js
+++ b/dom/nfc/NfcContentHelper.js
@@ -296,23 +296,32 @@ NfcContentHelper.prototype = {
             break;
           case NFC.PEER_EVENT_FOUND:
             this.eventListener.notifyPeerFound(result.sessionToken);
             break;
           case NFC.PEER_EVENT_LOST:
             this.eventListener.notifyPeerLost(result.sessionToken);
             break;
           case NFC.TAG_EVENT_FOUND:
-            let event = new NfcTagEvent(result.techList,
-                                        result.tagType,
-                                        result.maxNDEFSize,
-                                        result.isReadOnly,
-                                        result.isFormatable);
+            let ndefInfo = null;
+            if (result.tagType !== undefined &&
+                result.maxNDEFSize !== undefined &&
+                result.isReadOnly !== undefined &&
+                result.isFormatable !== undefined) {
+              ndefInfo = new TagNDEFInfo(result.tagType,
+                                         result.maxNDEFSize,
+                                         result.isReadOnly,
+                                         result.isFormatable);
+            }
 
-            this.eventListener.notifyTagFound(result.sessionToken, event, result.records);
+            let tagInfo = new TagInfo(result.techList);
+            this.eventListener.notifyTagFound(result.sessionToken,
+                                              tagInfo,
+                                              ndefInfo,
+                                              result.records);
             break;
           case NFC.TAG_EVENT_LOST:
             this.eventListener.notifyTagLost(result.sessionToken);
             break;
           case NFC.RF_EVENT_STATE_CHANGE:
             this._rfState = result.rfState;
             this.eventListener.notifyRFStateChange(this._rfState);
             break;
@@ -382,28 +391,35 @@ NfcContentHelper.prototype = {
     delete this._requestMap[requestId];
 
     // Privilaged status API. Always fire success to avoid using exposed props.
     // The receiver must check the boolean mapped status code to handle.
     callback.notifySuccessWithBoolean(!result.errorMsg);
   },
 };
 
-function NfcTagEvent(techList, tagType, maxNDEFSize, isReadOnly, isFormatable) {
-  this.techList = techList;
+function TagNDEFInfo(tagType, maxNDEFSize, isReadOnly, isFormatable) {
   this.tagType = tagType;
   this.maxNDEFSize = maxNDEFSize;
   this.isReadOnly = isReadOnly;
   this.isFormatable = isFormatable;
 }
-NfcTagEvent.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcTagEvent]),
+TagNDEFInfo.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsITagNDEFInfo]),
 
-  techList: null,
   tagType: null,
   maxNDEFSize: 0,
   isReadOnly: false,
   isFormatable: false
 };
 
+function TagInfo(techList) {
+  this.techList = techList;
+}
+TagInfo.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsITagInfo]),
+
+  techList: null,
+};
+
 if (NFC_ENABLED) {
   this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]);
 }
new file mode 100644
--- /dev/null
+++ b/dom/nfc/NfcContentHelper.manifest
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# NfcContentHelper.js
+component {4d72c120-da5f-11e1-9b23-0800200c9a66} NfcContentHelper.js
+contract @mozilla.org/nfc/content-helper;1 {4d72c120-da5f-11e1-9b23-0800200c9a66}
+category profile-after-change NfcContentHelper @mozilla.org/nfc/content-helper;1
--- a/dom/nfc/gonk/Nfc.manifest
+++ b/dom/nfc/gonk/Nfc.manifest
@@ -4,12 +4,8 @@
 #
 # Copyright © 2013 Deutsche Telekom, Inc.
 
 # Nfc.js
 component {2ff24790-5e74-11e1-b86c-0800200c9a66} Nfc.js
 contract @mozilla.org/nfc;1 {2ff24790-5e74-11e1-b86c-0800200c9a66}
 category profile-after-change Nfc @mozilla.org/nfc;1
 
-# NfcContentHelper.js
-component {4d72c120-da5f-11e1-9b23-0800200c9a66} NfcContentHelper.js
-contract @mozilla.org/nfc/content-helper;1 {4d72c120-da5f-11e1-9b23-0800200c9a66}
-category profile-after-change NfcContentHelper @mozilla.org/nfc/content-helper;1
--- a/dom/nfc/moz.build
+++ b/dom/nfc/moz.build
@@ -14,16 +14,17 @@ if CONFIG['MOZ_NFC']:
     EXPORTS.mozilla.dom += [
         'MozNDEFRecord.h',
     ]
     UNIFIED_SOURCES += [
         'MozNDEFRecord.cpp',
     ]
     EXTRA_COMPONENTS += [
         'NfcContentHelper.js',
+        'NfcContentHelper.manifest',
         'nsNfc.js',
         'nsNfc.manifest',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_NFC']:
     UNIFIED_SOURCES += [
         'gonk/NfcMessageHandler.cpp',
         'gonk/NfcService.cpp',
--- a/dom/nfc/nsINfcContentHelper.idl
+++ b/dom/nfc/nsINfcContentHelper.idl
@@ -2,46 +2,57 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIVariant;
 interface nsIDOMWindow;
 
-[scriptable, uuid(9b43bdda-52f4-4712-b28c-ad7cba736e14)]
-interface nsINfcTagEvent : nsISupports
+[scriptable, uuid(30d77baf-50ed-4a6b-ab75-25bade40977a)]
+interface nsITagInfo : nsISupports
 {
+  /**
+   * Array of technolgies supported. See NFCTechType in MozNFCTag.webidl
+   */
   readonly attribute nsIVariant techList;
+};
 
+[scriptable, uuid(74d70ebb-557f-4ac8-8296-7885961cd1dc)]
+interface nsITagNDEFInfo : nsISupports
+{
   // one of NFCTagType defined in MozNFCTag.webidl.
   readonly attribute DOMString tagType;
 
   readonly attribute long maxNDEFSize;
 
   readonly attribute boolean isReadOnly;
 
   readonly attribute boolean isFormatable;
 };
 
-[scriptable, uuid(fcbd98d6-3d04-4657-bd64-1164e311b399)]
+[scriptable, uuid(be09c440-8385-4210-bb7b-7d3333ec3d43)]
 interface nsINfcEventListener : nsISupports
 {
   /**
    * Callback function used to notify tagfound.
    *
    * @param sessionToken
    *        SessionToken received from parent process
-   * @param event
-   *        nsINfcTagFoundEvent received from parent process.
+   * @param tagInfo
+   *        nsITagInfo received from parent process.
+   * @param ndefInfo
+   *        nsITagNDEFInfo received from parent process, could be null if the
+   *        tag is not formated as NDEF.
    * @param ndefRecords
    *        NDEF records pre-read during tag-discovered.
    */
   void notifyTagFound(in DOMString sessionToken,
-                      in nsINfcTagEvent event,
+                      in nsITagInfo tagInfo,
+                      in nsITagNDEFInfo ndefInfo,
                       in nsIVariant ndefRecords);
 
   /**
    * Callback function used to notify taglost.
    *
    * @param sessionToken
    *        SessionToken received from parent process
    */
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -87,46 +87,48 @@ NfcCallback.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
                                          Ci.nsIObserver,
                                          Ci.nsINfcRequestCallback]),
 };
 
 /**
  * Implementation of NFCTag.
  *
- * @param window  global window object.
+ * @param window        global window object.
  * @param sessionToken  session token received from parent process.
- * @parem event   type of nsINfcTagEvent received from parent process.
+ * @param tagInfo       type of nsITagInfo received from parent process.
+ * @parem ndefInfo      type of nsITagNDEFInfo received from parent process.
  */
-function MozNFCTagImpl(window, sessionToken, event) {
+function MozNFCTagImpl(window, sessionToken, tagInfo, ndefInfo) {
   debug("In MozNFCTagImpl Constructor");
   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                              .getService(Ci.nsINfcContentHelper);
   this._window = window;
   this.session = sessionToken;
-  this.techList = event.techList;
-  this.type = event.tagType || null;
-  this.maxNDEFSize = event.maxNDEFSize || null;
-  this.isReadOnly = event.isReadOnly || null;
-  this.isFormatable = event.isFormatable || null;
-  this.canBeMadeReadOnly = this.type ?
-                             (this.type == "type1" || this.type == "type2" ||
-                              this.type == "mifare_classic") :
-                             null;
+  this.techList = tagInfo.techList;
+
+  if (ndefInfo) {
+    this.type = ndefInfo.tagType;
+    this.maxNDEFSize = ndefInfo.maxNDEFSize;
+    this.isReadOnly = ndefInfo.isReadOnly;
+    this.isFormatable = ndefInfo.isFormatable;
+    this.canBeMadeReadOnly = this.type == "type1" || this.type == "type2" ||
+                             this.type == "mifare_classic";
+  }
 }
 MozNFCTagImpl.prototype = {
   _nfcContentHelper: null,
   _window: null,
   session: null,
   techList: null,
   type: null,
-  maxNDEFSize: 0,
-  isReadOnly: false,
-  isFormatable: false,
-  canBeMadeReadOnly: false,
+  maxNDEFSize: null,
+  isReadOnly: null,
+  isFormatable: null,
+  canBeMadeReadOnly: null,
   isLost: false,
 
   // NFCTag interface:
   readNDEF: function readNDEF() {
     if (this.isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
     }
 
@@ -360,17 +362,17 @@ MozNFCImpl.prototype = {
     if (eventType !== "peerready") {
       return;
     }
 
     let appId = this._window.document.nodePrincipal.appId;
     this._nfcContentHelper.unregisterTargetForPeerReady(appId);
   },
 
-  notifyTagFound: function notifyTagFound(sessionToken, event, records) {
+  notifyTagFound: function notifyTagFound(sessionToken, tagInfo, ndefInfo, records) {
     if (this.hasDeadWrapper()) {
       dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
       return;
     }
 
     if (!this.eventService.hasListenersFor(this.__DOM_IMPL__, "tagfound")) {
       debug("ontagfound is not registered.");
       return;
@@ -378,17 +380,17 @@ MozNFCImpl.prototype = {
 
     if (!this.checkPermissions(["nfc"])) {
       return;
     }
 
     this.eventService.addSystemEventListener(this._window, "visibilitychange",
       this, /* useCapture */false);
 
-    let tagImpl = new MozNFCTagImpl(this._window, sessionToken, event);
+    let tagImpl = new MozNFCTagImpl(this._window, sessionToken, tagInfo, ndefInfo);
     let tag = this._window.MozNFCTag._create(this._window, tagImpl);
     this.nfcTag = tag;
 
     let length = records ? records.length : 0;
     let ndefRecords = records ? [] : null;
     for (let i = 0; i < length; i++) {
       let record = records[i];
       ndefRecords.push(new this._window.MozNDEFRecord({tnf: record.tnf,
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -23,17 +23,20 @@
 #include "nsIDOMElement.h"
 #include "prmem.h"
 #include "nsIContent.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsWrapperCacheInlines.h"
 #include "js/HashTable.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/dom/ScriptSettings.h"
-
+#include "mozilla/plugins/PluginAsyncSurrogate.h"
+
+using mozilla::plugins::AsyncNPObject;
+using mozilla::plugins::PluginAsyncSurrogate;
 
 #define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
 
 using namespace mozilla::plugins::parent;
 using namespace mozilla;
 
 #include "mozilla/plugins/PluginScriptableObjectParent.h"
 using mozilla::plugins::PluginScriptableObjectParent;
@@ -89,20 +92,34 @@ static int32_t sWrapperCount;
 
 // The runtime service used to register/unregister GC callbacks.
 nsCOMPtr<nsIJSRuntimeService> sCallbackRuntime;
 
 static nsTArray<NPObject*>* sDelayedReleases;
 
 namespace {
 
+inline void
+CastNPObject(NPObject *aObj, PluginScriptableObjectParent*& aActor,
+             PluginAsyncSurrogate*& aSurrogate)
+{
+  aActor = nullptr;
+  aSurrogate = nullptr;
+  if (aObj->_class == PluginScriptableObjectParent::GetClass()) {
+    aActor = static_cast<ParentNPObject*>(aObj)->parent;
+  } else if (aObj->_class == PluginAsyncSurrogate::GetClass()) {
+    aSurrogate = static_cast<AsyncNPObject*>(aObj)->mSurrogate;
+  }
+}
+
 inline bool
 NPObjectIsOutOfProcessProxy(NPObject *obj)
 {
-  return obj->_class == PluginScriptableObjectParent::GetClass();
+  return obj->_class == PluginScriptableObjectParent::GetClass() ||
+         obj->_class == PluginAsyncSurrogate::GetClass();
 }
 
 } // anonymous namespace
 
 // Helper class that reports any JS exceptions that were thrown while
 // the plugin executed JS.
 
 class AutoJSExceptionReporter
@@ -1384,25 +1401,33 @@ NPObjWrapper_GetProperty(JSContext *cx, 
   bool hasProperty, hasMethod;
 
   NPVariant npv;
   VOID_TO_NPVARIANT(npv);
 
   NPIdentifier identifier = JSIdToNPIdentifier(id);
 
   if (NPObjectIsOutOfProcessProxy(npobj)) {
-    PluginScriptableObjectParent* actor =
-      static_cast<ParentNPObject*>(npobj)->parent;
-
-    // actor may be null if the plugin crashed.
-    if (!actor)
+    PluginScriptableObjectParent* actor = nullptr;
+    PluginAsyncSurrogate* surrogate = nullptr;
+    CastNPObject(npobj, actor, surrogate);
+
+    // actor and surrogate may be null if the plugin crashed.
+    if (!actor && !surrogate)
       return false;
 
-    bool success = actor->GetPropertyHelper(identifier, &hasProperty,
-                                            &hasMethod, &npv);
+    bool success = false;
+    if (surrogate) {
+      success = surrogate->GetPropertyHelper(npobj, identifier, &hasProperty,
+                                             &hasMethod, &npv);
+    } else if (actor) {
+      success = actor->GetPropertyHelper(identifier, &hasProperty, &hasMethod,
+                                         &npv);
+    }
+
     if (!ReportExceptionIfPending(cx)) {
       if (success)
         _releasevariantvalue(&npv);
       return false;
     }
 
     if (success) {
       // We return NPObject Member class here to support ambiguous members.
@@ -2248,8 +2273,40 @@ NPObjectMember_Trace(JSTracer *trc, JSOb
   // There's no strong reference from our private data to the
   // NPObject, so make sure to mark the NPObject wrapper to keep the
   // NPObject alive as long as this NPObjectMember is alive.
   if (memberPrivate->npobjWrapper) {
     JS_CallObjectTracer(trc, &memberPrivate->npobjWrapper,
                         "NPObject Member => npobjWrapper");
   }
 }
+
+// static
+bool
+nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
+{
+  NPP npp = NPPStack::Peek();
+  dom::AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
+    return false;
+  }
+  JSContext *cx = jsapi.cx();
+
+  if (!npobj) {
+    ThrowJSException(cx,
+                     "Null npobj in nsJSObjWrapper::NP_HasOwnProperty!");
+
+    return false;
+  }
+
+  nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+  bool found, ok = false;
+
+  AutoJSExceptionReporter reporter(cx);
+  JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+  JSAutoCompartment ac(cx, jsobj);
+
+  NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+               "id must be either string or int!\n");
+  JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+  ok = ::JS_AlreadyHasOwnPropertyById(cx, jsobj, id, &found);
+  return ok && found;
+}
--- a/dom/plugins/base/nsJSNPRuntime.h
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -39,16 +39,17 @@ public:
 class nsJSObjWrapper : public NPObject
 {
 public:
   JS::Heap<JSObject *> mJSObj;
   const NPP mNpp;
 
   static NPObject *GetNewOrUsed(NPP npp, JSContext *cx,
                                 JS::Handle<JSObject*> obj);
+  static bool HasOwnProperty(NPObject* npobj, NPIdentifier npid);
 
 protected:
   explicit nsJSObjWrapper(NPP npp);
   ~nsJSObjWrapper();
 
   static NPObject * NP_Allocate(NPP npp, NPClass *aClass);
   static void NP_Deallocate(NPObject *obj);
   static void NP_Invalidate(NPObject *obj);
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -518,17 +518,17 @@ nsNPAPIPluginInstance::Start()
   this, &mNPP, mimetype, mode, quirkParamLength, error));
 
   if (NS_FAILED(newResult) || error != NPERR_NO_ERROR) {
     mRunning = DESTROYED;
     nsJSNPRuntime::OnPluginDestroy(&mNPP);
     return NS_ERROR_FAILURE;
   }
 
-  return NS_OK;
+  return newResult;
 }
 
 nsresult nsNPAPIPluginInstance::SetWindow(NPWindow* window)
 {
   // NPAPI plugins don't want a SetWindow(nullptr).
   if (!window || RUNNING != mRunning)
     return NS_OK;
 
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -326,76 +326,95 @@ nsNPAPIPluginStreamListener::OnStartBind
                           NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
   
   NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                  ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
                   this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url));
   
   if (error != NPERR_NO_ERROR)
     return NS_ERROR_FAILURE;
-  
-  switch(streamType)
+
+  if (streamType == nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN) {
+    SuspendRequest();
+  } else if (!SetStreamType(streamType, false)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+bool
+nsNPAPIPluginStreamListener::SetStreamType(uint16_t aType, bool aNeedsResume)
+{
+  switch(aType)
   {
     case NP_NORMAL:
-      mStreamType = NP_NORMAL; 
+      mStreamType = NP_NORMAL;
       break;
     case NP_ASFILEONLY:
-      mStreamType = NP_ASFILEONLY; 
+      mStreamType = NP_ASFILEONLY;
       break;
     case NP_ASFILE:
-      mStreamType = NP_ASFILE; 
+      mStreamType = NP_ASFILE;
       break;
     case NP_SEEK:
-      mStreamType = NP_SEEK; 
+      mStreamType = NP_SEEK;
       // Seekable streams should continue to exist even after OnStopRequest
       // is fired, so we AddRef ourself an extra time and Release when the
       // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never
       // calls NPN_DestroyStream the stream will be destroyed before the plugin
       // instance is destroyed.
       NS_ADDREF_THIS();
       break;
     default:
-      return NS_ERROR_FAILURE;
+      return false;
   }
-  
   mStreamStarted = true;
-  return NS_OK;
+  if (aNeedsResume) {
+    if (mStreamListenerPeer) {
+      mStreamListenerPeer->OnStreamTypeSet(mStreamType);
+    }
+    ResumeRequest();
+  }
+  return true;
 }
 
 void
 nsNPAPIPluginStreamListener::SuspendRequest()
 {
   NS_ASSERTION(!mIsSuspended,
                "Suspending a request that's already suspended!");
 
   nsresult rv = StartDataPump();
   if (NS_FAILED(rv))
     return;
-  
+
   mIsSuspended = true;
 
   if (mStreamListenerPeer) {
-   mStreamListenerPeer->SuspendRequests();
+    mStreamListenerPeer->SuspendRequests();
   }
 }
 
 void
 nsNPAPIPluginStreamListener::ResumeRequest()
 {
-  mStreamListenerPeer->ResumeRequests();
+  if (mStreamListenerPeer) {
+    mStreamListenerPeer->ResumeRequests();
+  }
   mIsSuspended = false;
 }
 
 nsresult
 nsNPAPIPluginStreamListener::StartDataPump()
 {
   nsresult rv;
   mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   // Start pumping data to the plugin every 100ms until it obeys and
   // eats the data.
   return mDataPumpTimer->InitWithCallback(this, 100,
                                           nsITimer::TYPE_REPEATING_SLACK);
 }
 
 void
 nsNPAPIPluginStreamListener::StopDataPump()
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.h
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h
@@ -78,16 +78,17 @@ public:
   nsresult OnDataAvailable(nsPluginStreamListenerPeer* streamPeer,
                            nsIInputStream* input,
                            uint32_t length);
   nsresult OnFileAvailable(nsPluginStreamListenerPeer* streamPeer, 
                            const char* fileName);
   nsresult OnStopBinding(nsPluginStreamListenerPeer* streamPeer, 
                          nsresult status);
   nsresult GetStreamType(int32_t *result);
+  bool SetStreamType(uint16_t aType, bool aNeedsResume = true);
 
   bool IsStarted();
   nsresult CleanUpStream(NPReason reason);
   void CallURLNotify(NPReason reason);
   void SetCallNotify(bool aCallNotify) { mCallNotify = aCallNotify; }
   void SuspendRequest();
   void ResumeRequest();
   nsresult StartDataPump();
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -45,16 +45,17 @@
 #include "nsIBlocklistService.h"
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/LoadInfo.h"
+#include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsISupportsPrimitives.h"
@@ -105,16 +106,17 @@
 
 #if MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla;
 using mozilla::TimeStamp;
 using mozilla::plugins::PluginTag;
+using mozilla::plugins::PluginAsyncSurrogate;
 
 // Null out a strong ref to a linked list iteratively to avoid
 // exhausting the stack (bug 486349).
 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
   {                                                                  \
     while (list_) {                                                  \
       type_ temp = list_->mNext_;                                    \
       list_->mNext_ = nullptr;                                        \
@@ -825,28 +827,27 @@ nsPluginHost::InstantiatePluginInstance(
       tagType != nsPluginTagType_Object) {
     return NS_ERROR_FAILURE;
   }
 
   rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
+  const bool isAsyncInit = (rv == NS_PLUGIN_INIT_PENDING);
 
   nsRefPtr<nsNPAPIPluginInstance> instance;
   rv = instanceOwner->GetInstance(getter_AddRefs(instance));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (instance) {
-    instanceOwner->CreateWidget();
-
-    // If we've got a native window, the let the plugin know about it.
-    instanceOwner->CallSetWindow();
+  // Async init plugins will initiate their own widget creation.
+  if (!isAsyncInit && instance) {
+    CreateWidget(instanceOwner);
   }
 
   // At this point we consider instantiation to be successful. Do not return an error.
   instanceOwner.forget(aOwner);
 
 #ifdef PLUGIN_LOGGING
   nsAutoCString urlSpec2;
   if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
@@ -3322,16 +3323,24 @@ nsresult nsPluginHost::NewPluginStreamLi
     return rv;
   }
 
   listener.forget(aStreamListener);
 
   return NS_OK;
 }
 
+void nsPluginHost::CreateWidget(nsPluginInstanceOwner* aOwner)
+{
+  aOwner->CreateWidget();
+
+  // If we've got a native window, the let the plugin know about it.
+  aOwner->CallSetWindow();
+}
+
 NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
                                     const char *aTopic,
                                     const char16_t *someData)
 {
   if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
     OnShutdown();
     UnloadPlugins();
     sInst->Release();
@@ -3949,16 +3958,22 @@ PRCList PluginDestructionGuard::sListHea
   PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
 
 PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance)
   : mInstance(aInstance)
 {
   Init();
 }
 
+PluginDestructionGuard::PluginDestructionGuard(PluginAsyncSurrogate *aSurrogate)
+  : mInstance(static_cast<nsNPAPIPluginInstance*>(aSurrogate->GetNPP()->ndata))
+{
+  InitAsync();
+}
+
 PluginDestructionGuard::PluginDestructionGuard(NPP npp)
   : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata) : nullptr)
 {
   Init();
 }
 
 PluginDestructionGuard::~PluginDestructionGuard()
 {
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -25,16 +25,22 @@
 #include "nsITimer.h"
 #include "nsPluginTags.h"
 #include "nsPluginPlayPreviewInfo.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIIDNService.h"
 #include "nsCRT.h"
 #include "mozilla/plugins/PluginTypes.h"
 
+namespace mozilla {
+namespace plugins {
+class PluginAsyncSurrogate;
+} // namespace mozilla
+} // namespace plugins
+
 class nsNPAPIPlugin;
 class nsIComponentManager;
 class nsIFile;
 class nsIChannel;
 class nsPluginNativeWindow;
 class nsObjectLoadingContent;
 class nsPluginInstanceOwner;
 class nsPluginUnloadRunnable;
@@ -198,16 +204,18 @@ public:
   nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
   nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
   void NotifyContentModuleDestroyed(uint32_t aPluginId);
 
   nsresult NewPluginStreamListener(nsIURI* aURL,
                                    nsNPAPIPluginInstance* aInstance,
                                    nsIStreamListener **aStreamListener);
 
+  void CreateWidget(nsPluginInstanceOwner* aOwner);
+
 private:
   friend class nsPluginUnloadRunnable;
 
   nsresult
   TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
 
   nsPluginTag*
   FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
@@ -328,21 +336,21 @@ private:
 
   static nsIFile *sPluginTempDir;
 
   // We need to hold a global ptr to ourselves because we register for
   // two different CIDs for some reason...
   static nsPluginHost* sInst;
 };
 
-class MOZ_STACK_CLASS PluginDestructionGuard : protected PRCList
+class PluginDestructionGuard : protected PRCList
 {
 public:
   explicit PluginDestructionGuard(nsNPAPIPluginInstance *aInstance);
-
+  explicit PluginDestructionGuard(mozilla::plugins::PluginAsyncSurrogate *aSurrogate);
   explicit PluginDestructionGuard(NPP npp);
 
   ~PluginDestructionGuard();
 
   static bool DelayDestroy(nsNPAPIPluginInstance *aInstance);
 
 protected:
   void Init()
@@ -350,15 +358,27 @@ protected:
     NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
 
     mDelayedDestroy = false;
 
     PR_INIT_CLIST(this);
     PR_INSERT_BEFORE(this, &sListHead);
   }
 
+  void InitAsync()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+
+    mDelayedDestroy = false;
+
+    PR_INIT_CLIST(this);
+    // Instances with active surrogates must be inserted *after* sListHead so
+    // that they appear to be at the bottom of the stack
+    PR_INSERT_AFTER(this, &sListHead);
+  }
+
   nsRefPtr<nsNPAPIPluginInstance> mInstance;
   bool mDelayedDestroy;
 
   static PRCList sListHead;
 };
 
 #endif // nsPluginHost_h_
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1447,16 +1447,38 @@ void nsPluginInstanceOwner::ExitFullScre
   if (sFullScreenInstance && sFullScreenInstance->mInstance &&
       env->IsSameObject(view, (jobject)sFullScreenInstance->mInstance->GetJavaSurface())) {
     sFullScreenInstance->ExitFullScreen();
   }
 }
 
 #endif
 
+void
+nsPluginInstanceOwner::NotifyHostAsyncInitFailed()
+{
+  nsCOMPtr<nsIObjectLoadingContent> content = do_QueryInterface(mContent);
+  content->StopPluginInstance();
+}
+
+void
+nsPluginInstanceOwner::NotifyHostCreateWidget()
+{
+  mPluginHost->CreateWidget(this);
+#ifdef XP_MACOSX
+  FixUpPluginWindow(ePluginPaintEnable);
+#else
+  if (mPluginFrame) {
+    mPluginFrame->InvalidateFrame();
+  } else {
+    CallSetWindow();
+  }
+#endif
+}
+
 nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
 {
 #ifdef MOZ_WIDGET_ANDROID
   if (mInstance) {
     ANPEvent event;
     event.inSize = sizeof(ANPEvent);
     event.eventType = kLifecycle_ANPEventType;
 
@@ -3164,16 +3186,20 @@ nsPluginInstanceOwner::UpdateDocumentAct
     mWidget->Enable(aIsActive);
   }
 #endif // #ifndef XP_MACOSX
 }
 
 NS_IMETHODIMP
 nsPluginInstanceOwner::CallSetWindow()
 {
+  if (!mWidgetCreationComplete) {
+    // No widget yet, we can't run this code
+    return NS_OK;
+  }
   if (mPluginFrame) {
     mPluginFrame->CallSetWindow(false);
   } else if (mInstance) {
     if (UseAsyncRendering()) {
       mInstance->AsyncSetWindow(mPluginWindow);
     } else {
       mInstance->SetWindow(mPluginWindow);
     }
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -250,17 +250,20 @@ public:
   void Invalidate();
 
   void RequestFullScreen();
   void ExitFullScreen();
 
   // Called from AndroidJNI when we removed the fullscreen view.
   static void ExitFullScreen(jobject view);
 #endif
-  
+
+  void NotifyHostAsyncInitFailed();
+  void NotifyHostCreateWidget();
+
 private:
   virtual ~nsPluginInstanceOwner();
 
   // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
   bool IsUpToDate()
   {
     nsIntSize size;
     return NS_SUCCEEDED(mInstance->GetImageSize(&size)) &&
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -261,16 +261,17 @@ nsPluginStreamListenerPeer::nsPluginStre
   mStartBinding = false;
   mAbort = false;
   mRequestFailed = false;
 
   mPendingRequests = 0;
   mHaveFiredOnStartRequest = false;
   mDataForwardToRequest = nullptr;
 
+  mUseLocalCache = false;
   mSeekable = false;
   mModified = 0;
   mStreamOffset = 0;
   mStreamComplete = 0;
 }
 
 nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
 {
@@ -528,17 +529,19 @@ nsPluginStreamListenerPeer::OnStartReque
          ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
           this, request, aContentType.get(), mURLSpec.get()));
 
   PR_LogFlush();
 #endif
 
   // Set up the stream listener...
   rv = SetUpStreamListener(request, aURL);
-  if (NS_FAILED(rv)) return rv;
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
                                                      nsISupports* aContext,
                                                      uint64_t aProgress,
                                                      uint64_t aProgressMax)
@@ -1041,18 +1044,16 @@ nsresult nsPluginStreamListenerPeer::Set
       return NS_ERROR_FAILURE;
     }
 
     mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get());
   }
 
   mPStreamListener->SetStreamListenerPeer(this);
 
-  bool useLocalCache = false;
-
   // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
 
   /*
    * Assumption
    * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
    * called, all the headers have been read.
@@ -1098,17 +1099,17 @@ nsresult nsPluginStreamListenerPeer::Set
     // uncompressed data, so it can't make meaningful range requests on a
     // compressed entity.  Also, we force the plugin to use
     // nsPluginStreamType_AsFile stream type and we have to save decompressed
     // file into local plugin cache, because necko cache contains original
     // compressed file.
     nsAutoCString contentEncoding;
     if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
                                                     contentEncoding))) {
-      useLocalCache = true;
+      mUseLocalCache = true;
     } else {
       // set seekability (seekable if the stream has a known length and if the
       // http server accepts byte ranges).
       uint32_t length;
       GetLength(&length);
       if (length) {
         nsAutoCString range;
         if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) &&
@@ -1127,40 +1128,57 @@ nsresult nsPluginStreamListenerPeer::Set
       PR_ParseTimeString(lastModified.get(), true, &time64);  //convert string time to integer time
 
       // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
       double fpTime = double(time64);
       mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
     }
   }
 
+  MOZ_ASSERT(!mRequest);
+  mRequest = request;
+
   rv = mPStreamListener->OnStartBinding(this);
 
   mStartBinding = true;
 
   if (NS_FAILED(rv))
     return rv;
 
-  mPStreamListener->GetStreamType(&mStreamType);
+  int32_t streamType = NP_NORMAL;
+  mPStreamListener->GetStreamType(&streamType);
 
-  if (!useLocalCache && mStreamType >= NP_ASFILE) {
-    // check it out if this is not a file channel.
-    nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
-    if (!fileChannel) {
-        useLocalCache = true;
-    }
-  }
-
-  if (useLocalCache) {
-    SetupPluginCacheFile(channel);
+  if (streamType != STREAM_TYPE_UNKNOWN) {
+    OnStreamTypeSet(streamType);
   }
 
   return NS_OK;
 }
 
+void
+nsPluginStreamListenerPeer::OnStreamTypeSet(const int32_t aStreamType)
+{
+  MOZ_ASSERT(mRequest);
+  mStreamType = aStreamType;
+  if (!mUseLocalCache && mStreamType >= NP_ASFILE) {
+    // check it out if this is not a file channel.
+    nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(mRequest);
+    if (!fileChannel) {
+      mUseLocalCache = true;
+    }
+  }
+
+  if (mUseLocalCache) {
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
+    SetupPluginCacheFile(channel);
+  }
+}
+
+const int32_t nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN = UINT16_MAX;
+
 nsresult
 nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
 {
   nsresult rv;
   if (!mPStreamListener)
     return NS_ERROR_FAILURE;
 
   nsAutoCString path;
--- a/dom/plugins/base/nsPluginStreamListenerPeer.h
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.h
@@ -123,16 +123,21 @@ public:
   }
 
   void ResumeRequests() {
     nsCOMArray<nsIRequest> requestsCopy(mRequests);
     for (int32_t i = 0; i < requestsCopy.Count(); ++i)
       requestsCopy[i]->Resume();
   }
 
+  // Called by nsNPAPIPluginStreamListener
+  void OnStreamTypeSet(const int32_t aStreamType);
+
+  static const int32_t STREAM_TYPE_UNKNOWN;
+
 private:
   nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
   nsresult SetupPluginCacheFile(nsIChannel* channel);
   nsresult GetInterfaceGlobal(const nsIID& aIID, void** result);
 
   nsCOMPtr<nsIURI> mURL;
   nsCString mURLSpec; // Have to keep this member because GetURL hands out char*
   nsRefPtr<nsNPAPIPluginStreamListener> mPStreamListener;
@@ -154,16 +159,18 @@ private:
 
   // local cached file, we save the content into local cache if browser cache is not available,
   // or plugin asks stream as file and it expects file extension until bug 90558 got fixed
   nsRefPtr<CachedFileHolder> mLocalCachedFileHolder;
   nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
   nsDataHashtable<nsUint32HashKey, uint32_t>* mDataForwardToRequest;
 
   nsCString mContentType;
+  bool mUseLocalCache;
+  nsCOMPtr<nsIRequest> mRequest;
   bool mSeekable;
   uint32_t mModified;
   nsRefPtr<nsNPAPIPluginInstance> mPluginInstance;
   int32_t mStreamOffset;
   bool mStreamComplete;
 
 public:
   bool                    mAbort;
--- a/dom/plugins/ipc/BrowserStreamChild.cpp
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -12,47 +12,45 @@
 namespace mozilla {
 namespace plugins {
 
 BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
                                        const nsCString& url,
                                        const uint32_t& length,
                                        const uint32_t& lastmodified,
                                        StreamNotifyChild* notifyData,
-                                       const nsCString& headers,
-                                       const nsCString& mimeType,
-                                       const bool& seekable,
-                                       NPError* rv,
-                                       uint16_t* stype)
+                                       const nsCString& headers)
   : mInstance(instance)
   , mStreamStatus(kStreamOpen)
   , mDestroyPending(NOT_DESTROYED)
   , mNotifyPending(false)
   , mStreamAsFilePending(false)
   , mInstanceDying(false)
   , mState(CONSTRUCTING)
   , mURL(url)
   , mHeaders(headers)
   , mStreamNotify(notifyData)
   , mDeliveryTracker(MOZ_THIS_IN_INITIALIZER_LIST())
 {
-  PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s, %s)", FULLFUNCTION,
+  PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s)", FULLFUNCTION,
                     url.get(), length, lastmodified, (void*) notifyData,
-                    headers.get(), mimeType.get()));
+                    headers.get()));
 
   AssertPluginThread();
 
   memset(&mStream, 0, sizeof(mStream));
   mStream.ndata = static_cast<AStream*>(this);
   mStream.url = NullableStringGet(mURL);
   mStream.end = length;
   mStream.lastmodified = lastmodified;
   mStream.headers = NullableStringGet(mHeaders);
-  if (notifyData)
+  if (notifyData) {
     mStream.notifyData = notifyData->mClosure;
+    notifyData->SetAssociatedStream(this);
+  }
 }
 
 NPError
 BrowserStreamChild::StreamConstructed(
             const nsCString& mimeType,
             const bool& seekable,
             uint16_t* stype)
 {
@@ -63,19 +61,16 @@ BrowserStreamChild::StreamConstructed(
     &mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
     &mStream, seekable, stype);
   if (rv != NPERR_NO_ERROR) {
     mState = DELETING;
     mStreamNotify = nullptr;
   }
   else {
     mState = ALIVE;
-
-    if (mStreamNotify)
-      mStreamNotify->SetAssociatedStream(this);
   }
 
   return rv;
 }
 
 BrowserStreamChild::~BrowserStreamChild()
 {
   NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
--- a/dom/plugins/ipc/BrowserStreamChild.h
+++ b/dom/plugins/ipc/BrowserStreamChild.h
@@ -18,21 +18,17 @@ class StreamNotifyChild;
 class BrowserStreamChild : public PBrowserStreamChild, public AStream
 {
 public:
   BrowserStreamChild(PluginInstanceChild* instance,
                      const nsCString& url,
                      const uint32_t& length,
                      const uint32_t& lastmodified,
                      StreamNotifyChild* notifyData,
-                     const nsCString& headers,
-                     const nsCString& mimeType,
-                     const bool& seekable,
-                     NPError* rv,
-                     uint16_t* stype);
+                     const nsCString& headers);
   virtual ~BrowserStreamChild();
 
   virtual bool IsBrowserStream() MOZ_OVERRIDE { return true; }
 
   NPError StreamConstructed(
             const nsCString& mimeType,
             const bool& seekable,
             uint16_t* stype);
--- a/dom/plugins/ipc/BrowserStreamParent.cpp
+++ b/dom/plugins/ipc/BrowserStreamParent.cpp
@@ -1,53 +1,102 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "BrowserStreamParent.h"
+#include "PluginAsyncSurrogate.h"
 #include "PluginInstanceParent.h"
 #include "nsNPAPIPlugin.h"
 
 #include "mozilla/unused.h"
 
 // How much data are we willing to send across the wire
 // in one chunk?
 static const int32_t kSendDataChunk = 0x4000;
 
 namespace mozilla {
 namespace plugins {
 
 BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp,
                                          NPStream* stream)
   : mNPP(npp)
   , mStream(stream)
-  , mState(ALIVE)
+  , mDeferredDestroyReason(NPRES_DONE)
+  , mState(INITIALIZING)
 {
   mStream->pdata = static_cast<AStream*>(this);
+  nsNPAPIStreamWrapper* wrapper =
+    reinterpret_cast<nsNPAPIStreamWrapper*>(mStream->ndata);
+  if (wrapper) {
+    mStreamListener = wrapper->GetStreamListener();
+  }
 }
 
 BrowserStreamParent::~BrowserStreamParent()
 {
 }
 
 void
 BrowserStreamParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Implement me! Bug 1005159
 }
 
 bool
+BrowserStreamParent::RecvAsyncNPP_NewStreamResult(const NPError& rv,
+                                                  const uint16_t& stype)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  PluginAsyncSurrogate* surrogate = mNPP->GetAsyncSurrogate();
+  MOZ_ASSERT(surrogate);
+  surrogate->AsyncCallArriving();
+  nsRefPtr<nsNPAPIPluginStreamListener> streamListener = mStreamListener.forget();
+  if (mState == DEFERRING_DESTROY) {
+    // We've been asked to destroy ourselves before init was complete.
+    mState = DYING;
+    unused << SendNPP_DestroyStream(mDeferredDestroyReason);
+    return true;
+  }
+
+  NPError error = rv;
+  if (error == NPERR_NO_ERROR) {
+    if (!streamListener) {
+      return false;
+    }
+    if (streamListener->SetStreamType(stype)) {
+      mState = ALIVE;
+    } else {
+      error = NPERR_GENERIC_ERROR;
+    }
+  }
+
+  if (error != NPERR_NO_ERROR) {
+    // We need to clean up the stream
+    parent::_destroystream(mNPP->GetNPP(), mStream, NPRES_DONE);
+    unused << PBrowserStreamParent::Send__delete__(this);
+  }
+
+  return true;
+}
+
+bool
 BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
                                            NPError* result)
 {
   PLUGIN_LOG_DEBUG_FUNCTION;
 
   switch (mState) {
+  case INITIALIZING:
+    NS_ERROR("Requesting a read before initialization has completed");
+    *result = NPERR_GENERIC_ERROR;
+    return false;
+
   case ALIVE:
     break;
 
   case DYING:
     *result = NPERR_GENERIC_ERROR;
     return true;
 
   default:
@@ -90,19 +139,26 @@ BrowserStreamParent::RecvNPN_DestroyStre
 
   mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
   return true;
 }
 
 void
 BrowserStreamParent::NPP_DestroyStream(NPReason reason)
 {
-  NS_ASSERTION(ALIVE == mState, "NPP_DestroyStream called twice?");
-  mState = DYING;
-  unused << SendNPP_DestroyStream(reason);
+  NS_ASSERTION(ALIVE == mState || INITIALIZING == mState,
+               "NPP_DestroyStream called twice?");
+  bool stillInitializing = INITIALIZING == mState;
+  if (stillInitializing) {
+    mState = DEFERRING_DESTROY;
+    mDeferredDestroyReason = reason;
+  } else {
+    mState = DYING;
+    unused << SendNPP_DestroyStream(reason);
+  }
 }
 
 bool
 BrowserStreamParent::RecvStreamDestroyed()
 {
   if (DYING != mState) {
     NS_ERROR("Unexpected state");
     return false;
@@ -112,16 +168,19 @@ BrowserStreamParent::RecvStreamDestroyed
 
   mState = DELETING;
   return Send__delete__(this);
 }
 
 int32_t
 BrowserStreamParent::WriteReady()
 {
+  if (mState == INITIALIZING) {
+    return 0;
+  }
   return kSendDataChunk;
 }
 
 int32_t
 BrowserStreamParent::Write(int32_t offset,
                            int32_t len,
                            void* buffer)
 {
--- a/dom/plugins/ipc/BrowserStreamParent.h
+++ b/dom/plugins/ipc/BrowserStreamParent.h
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_plugins_BrowserStreamParent_h
 #define mozilla_plugins_BrowserStreamParent_h
 
 #include "mozilla/plugins/PBrowserStreamParent.h"
 #include "mozilla/plugins/AStream.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginStreamListenerPeer.h"
 
 namespace mozilla {
 namespace plugins {
 
 class PluginInstanceParent;
 
 class BrowserStreamParent : public PBrowserStreamParent, public AStream
 {
@@ -23,37 +25,52 @@ public:
   BrowserStreamParent(PluginInstanceParent* npp,
                       NPStream* stream);
   virtual ~BrowserStreamParent();
 
   virtual bool IsBrowserStream() MOZ_OVERRIDE { return true; }
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
+  virtual bool RecvAsyncNPP_NewStreamResult(
+            const NPError& rv,
+            const uint16_t& stype) MOZ_OVERRIDE;
+
   virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges,
                                      NPError* result) MOZ_OVERRIDE;
 
   virtual bool RecvNPN_DestroyStream(const NPReason& reason) MOZ_OVERRIDE;
 
   virtual bool RecvStreamDestroyed() MOZ_OVERRIDE;
 
   int32_t WriteReady();
   int32_t Write(int32_t offset, int32_t len, void* buffer);
   void StreamAsFile(const char* fname);
 
   void NPP_DestroyStream(NPReason reason);
 
+  void SetAlive()
+  {
+    if (mState == INITIALIZING) {
+      mState = ALIVE;
+    }
+  }
+
 private:
   using PBrowserStreamParent::SendNPP_DestroyStream;
 
   PluginInstanceParent* mNPP;
   NPStream* mStream;
   nsCOMPtr<nsISupports> mStreamPeer;
+  nsRefPtr<nsNPAPIPluginStreamListener> mStreamListener;
+  NPReason mDeferredDestroyReason;
 
   enum {
+    INITIALIZING,
+    DEFERRING_DESTROY,
     ALIVE,
     DYING,
     DELETING
   } mState;
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PBrowserStream.ipdl
+++ b/dom/plugins/ipc/PBrowserStream.ipdl
@@ -31,16 +31,17 @@ child:
   /**
    * NPP_DestroyStream may race with other messages: the child acknowledges
    * the message with StreamDestroyed before this actor is deleted.
    */
   async NPP_DestroyStream(NPReason reason);
   async __delete__();
 
 parent:
+  async AsyncNPP_NewStreamResult(NPError rv, uint16_t stype);
   intr NPN_RequestRead(IPCByteRanges ranges)
     returns (NPError result);
   async NPN_DestroyStream(NPReason reason);
   async StreamDestroyed();
 
 /*
   TODO: turn on state machine.
 
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -202,31 +202,39 @@ parent:
 
   async RedrawPlugin();
 
   // Send notification that a plugin tried to negotiate Carbon NPAPI so that
   // users can be notified that restarting the browser in i386 mode may allow
   // them to use the plugin.
   sync NegotiatedCarbon();
 
+  // Notifies the parent of its NPP_New result code.
+  async AsyncNPP_NewResult(NPError aResult);
+
 both:
   async PPluginScriptableObject();
 
 child:
   /* NPP_NewStream */
-  intr PBrowserStream(nsCString url,
-                     uint32_t length,
-                     uint32_t lastmodified,
-                     nullable PStreamNotify notifyData,
-                     nsCString headers,
-                     nsCString mimeType,
-                     bool seekable)
+  async PBrowserStream(nsCString url,
+                       uint32_t length,
+                       uint32_t lastmodified,
+                       nullable PStreamNotify notifyData,
+                       nsCString headers);
+
+  // Implements the legacy (synchronous) version of NPP_NewStream for when
+  // async plugin init is preffed off.
+  intr NPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable)
     returns (NPError rv,
              uint16_t stype);
 
+  // Implements the async plugin init version of NPP_NewStream.
+  async AsyncNPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable);
+
 parent:
   /* NPN_NewStream */
   intr PPluginStream(nsCString mimeType,
                     nsCString target)
     returns (NPError result);
 
 parent:
   intr PluginFocusChange(bool gotFocus);
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -49,22 +49,31 @@ child:
 
   // Forces the child process to update its plugin function table.
   intr NP_GetEntryPoints()
     returns (NPError rv);
 
   intr NP_Initialize(PluginSettings settings)
     returns (NPError rv);
 
-  intr PPluginInstance(nsCString aMimeType,
-                      uint16_t aMode,
-                      nsCString[] aNames,
-                      nsCString[] aValues)
+  async AsyncNP_Initialize(PluginSettings settings);
+
+  async PPluginInstance(nsCString aMimeType,
+                        uint16_t aMode,
+                        nsCString[] aNames,
+                        nsCString[] aValues);
+
+  // Implements the synchronous version of NPP_New for when async plugin init
+  // is preffed off.
+  intr SyncNPP_New(PPluginInstance aActor)
     returns (NPError rv);
 
+  // Implements the async plugin init version of NPP_New.
+  async AsyncNPP_New(PPluginInstance aActor);
+
   intr NP_Shutdown()
     returns (NPError rv);
 
   intr OptionalFunctionsSupported()
     returns (bool aURLRedirectNotify, bool aClearSiteData,
              bool aGetSitesWithData);
 
   intr NPP_ClearSiteData(nsCString site, uint64_t flags, uint64_t maxAge)
@@ -84,22 +93,25 @@ child:
     returns (NativeThreadId tid, uint32_t processType);
 
   /**
    * Control the Gecko Profiler in the plugin process.
    */
   async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                       nsCString[] aThreadNameFilters);
   async StopProfiler();
+
   intr GetProfile()
     returns (nsCString aProfile);
 
   async SettingChanged(PluginSettings settings);
 
 parent:
+  async NP_InitializeResult(NPError aError);
+
   /**
    * This message is only used on X11 platforms.
    *
    * Send a dup of the plugin process's X socket to the parent
    * process.  In theory, this scheme keeps the plugin's X resources
    * around until after both the plugin process shuts down *and* the
    * parent process closes the dup fd.  This is used to prevent the
    * parent process from crashing on X errors if, e.g., the plugin
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginAsyncSurrogate.cpp
@@ -0,0 +1,882 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PluginAsyncSurrogate.h"
+
+#include "base/message_loop.h"
+#include "base/message_pump_default.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/plugins/PluginModuleParent.h"
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+#include "mozilla/Telemetry.h"
+#include "nsJSNPRuntime.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "npruntime.h"
+#include "nsThreadUtils.h"
+#include "PluginMessageUtils.h"
+
+namespace mozilla {
+namespace plugins {
+
+AsyncNPObject::AsyncNPObject(PluginAsyncSurrogate* aSurrogate)
+  : NPObject()
+  , mSurrogate(aSurrogate)
+  , mRealObject(nullptr)
+{
+}
+
+AsyncNPObject::~AsyncNPObject()
+{
+  if (mRealObject) {
+    parent::_releaseobject(mRealObject);
+    mRealObject = nullptr;
+  }
+}
+
+NPObject*
+AsyncNPObject::GetRealObject()
+{
+  if (mRealObject) {
+    return mRealObject;
+  }
+  PluginInstanceParent* instance = PluginInstanceParent::Cast(mSurrogate->GetNPP());
+  if (!instance) {
+    return nullptr;
+  }
+  NPError err = instance->NPP_GetValue(NPPVpluginScriptableNPObject,
+                                       &mRealObject);
+  if (err != NPERR_NO_ERROR) {
+    return nullptr;
+  }
+  return mRealObject;
+}
+
+class MOZ_STACK_CLASS RecursionGuard
+{
+public:
+  RecursionGuard()
+    : mIsRecursive(sHasEntered)
+  {
+    if (!mIsRecursive) {
+      sHasEntered = true;
+    }
+  }
+
+  ~RecursionGuard()
+  {
+    if (!mIsRecursive) {
+      sHasEntered = false;
+    }
+  }
+
+  inline bool
+  IsRecursive()
+  {
+    return mIsRecursive;
+  }
+
+private:
+  bool        mIsRecursive;
+  static bool sHasEntered;
+};
+
+bool RecursionGuard::sHasEntered = false;
+
+PluginAsyncSurrogate::PluginAsyncSurrogate(PluginModuleParent* aParent)
+  : mParent(aParent)
+  , mInstance(nullptr)
+  , mMode(0)
+  , mWindow(nullptr)
+  , mAcceptCalls(false)
+  , mInstantiated(false)
+  , mAsyncSetWindow(false)
+  , mInitCancelled(false)
+  , mAsyncCallsInFlight(0)
+{
+  MOZ_ASSERT(aParent);
+}
+
+PluginAsyncSurrogate::~PluginAsyncSurrogate()
+{
+}
+
+bool
+PluginAsyncSurrogate::Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode,
+                           int16_t aArgc, char* aArgn[], char* aArgv[])
+{
+  mMimeType = aPluginType;
+  mInstance = aInstance;
+  mMode = aMode;
+  for (int i = 0; i < aArgc; ++i) {
+    mNames.AppendElement(NullableString(aArgn[i]));
+    mValues.AppendElement(NullableString(aArgv[i]));
+  }
+  return true;
+}
+
+/* static */ bool
+PluginAsyncSurrogate::Create(PluginModuleParent* aParent, NPMIMEType aPluginType,
+                             NPP aInstance, uint16_t aMode, int16_t aArgc,
+                             char* aArgn[], char* aArgv[])
+{
+  nsRefPtr<PluginAsyncSurrogate> surrogate(new PluginAsyncSurrogate(aParent));
+  if (!surrogate->Init(aPluginType, aInstance, aMode, aArgc, aArgn, aArgv)) {
+    return false;
+  }
+  PluginAsyncSurrogate* rawSurrogate = nullptr;
+  surrogate.forget(&rawSurrogate);
+  aInstance->pdata = static_cast<PluginDataResolver*>(rawSurrogate);
+  return true;
+}
+
+/* static */ PluginAsyncSurrogate*
+PluginAsyncSurrogate::Cast(NPP aInstance)
+{
+  MOZ_ASSERT(aInstance);
+  PluginDataResolver* resolver =
+    reinterpret_cast<PluginDataResolver*>(aInstance->pdata);
+  if (!resolver) {
+    return nullptr;
+  }
+  return resolver->GetAsyncSurrogate();
+}
+
+nsresult
+PluginAsyncSurrogate::NPP_New(NPError* aError)
+{
+  nsresult rv = mParent->NPP_NewInternal(mMimeType.BeginWriting(), mInstance,
+                                         mMode, mNames, mValues, nullptr,
+                                         aError);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+void
+PluginAsyncSurrogate::NP_GetEntryPoints(NPPluginFuncs* aFuncs)
+{
+  aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+  aFuncs->destroy = &NPP_Destroy;
+  aFuncs->getvalue = &NPP_GetValue;
+  aFuncs->setvalue = &NPP_SetValue;
+  aFuncs->newstream = &NPP_NewStream;
+  aFuncs->setwindow = &NPP_SetWindow;
+  aFuncs->writeready = &NPP_WriteReady;
+  aFuncs->event = &NPP_HandleEvent;
+  // We need to set these so that content code doesn't make assumptions
+  // about these operations not being supported
+  aFuncs->write = &PluginModuleParent::NPP_Write;
+  aFuncs->asfile = &PluginModuleParent::NPP_StreamAsFile;
+  aFuncs->destroystream = &PluginModuleParent::NPP_DestroyStream;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave)
+{
+  if (!WaitForInit()) {
+    return NPERR_GENERIC_ERROR;
+  }
+  return PluginModuleParent::NPP_Destroy(mInstance, aSave);
+}
+
+NPError
+PluginAsyncSurrogate::NPP_GetValue(NPPVariable aVariable, void* aRetval)
+{
+  if (aVariable != NPPVpluginScriptableNPObject) {
+    if (!WaitForInit()) {
+      return NPERR_GENERIC_ERROR;
+    }
+    PluginInstanceParent* instance = PluginInstanceParent::Cast(mInstance);
+    MOZ_ASSERT(instance);
+    return instance->NPP_GetValue(aVariable, aRetval);
+  }
+
+  NPObject* npobject = parent::_createobject(mInstance,
+                                             const_cast<NPClass*>(GetClass()));
+  MOZ_ASSERT(npobject);
+  MOZ_ASSERT(npobject->_class == GetClass());
+  MOZ_ASSERT(npobject->referenceCount == 1);
+  *(NPObject**)aRetval = npobject;
+  return npobject ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_SetValue(NPNVariable aVariable, void* aValue)
+{
+  if (!WaitForInit()) {
+    return NPERR_GENERIC_ERROR;
+  }
+  return PluginModuleParent::NPP_SetValue(mInstance, aVariable, aValue);
+}
+
+NPError
+PluginAsyncSurrogate::NPP_NewStream(NPMIMEType aType, NPStream* aStream,
+                                    NPBool aSeekable, uint16_t* aStype)
+{
+  mPendingNewStreamCalls.AppendElement(PendingNewStreamCall(aType, aStream,
+                                                            aSeekable));
+  if (aStype) {
+    *aStype = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
+  }
+  return NPERR_NO_ERROR;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_SetWindow(NPWindow* aWindow)
+{
+  mWindow = aWindow;
+  mAsyncSetWindow = false;
+  return NPERR_NO_ERROR;
+}
+
+nsresult
+PluginAsyncSurrogate::AsyncSetWindow(NPWindow* aWindow)
+{
+  mWindow = aWindow;
+  mAsyncSetWindow = true;
+  return NS_OK;
+}
+
+void
+PluginAsyncSurrogate::NPP_Print(NPPrint* aPrintInfo)
+{
+  // Do nothing, we've got nothing to print right now
+}
+
+int16_t
+PluginAsyncSurrogate::NPP_HandleEvent(void* event)
+{
+  // Drop the event -- the plugin isn't around to handle it
+  return false;
+}
+
+int32_t
+PluginAsyncSurrogate::NPP_WriteReady(NPStream* aStream)
+{
+  // We'll tell the browser to retry in a bit. Eventually NPP_WriteReady
+  // will resolve to the plugin's NPP_WriteReady and this should all just work.
+  return 0;
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_Destroy(NPP aInstance, NPSavedData** aSave)
+{
+  PluginAsyncSurrogate* rawSurrogate = Cast(aInstance);
+  MOZ_ASSERT(rawSurrogate);
+  PluginModuleParent* module = rawSurrogate->GetParent();
+  if (module && !module->IsInitialized()) {
+    // Take ownership of pdata's surrogate since we're going to release it
+    nsRefPtr<PluginAsyncSurrogate> surrogate(dont_AddRef(rawSurrogate));
+    aInstance->pdata = nullptr;
+    // We haven't actually called NPP_New yet, so we should remove the
+    // surrogate for this instance.
+    bool removeOk = module->RemovePendingSurrogate(surrogate);
+    MOZ_ASSERT(removeOk);
+    if (!removeOk) {
+      return NPERR_GENERIC_ERROR;
+    }
+    surrogate->mInitCancelled = true;
+    return NPERR_NO_ERROR;
+  }
+  return rawSurrogate->NPP_Destroy(aSave);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_GetValue(NPP aInstance, NPPVariable aVariable,
+                                   void* aRetval)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  return surrogate->NPP_GetValue(aVariable, aRetval);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_SetValue(NPP aInstance, NPNVariable aVariable,
+                                   void* aValue)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  return surrogate->NPP_SetValue(aVariable, aValue);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_NewStream(NPP aInstance, NPMIMEType aType,
+                                    NPStream* aStream, NPBool aSeekable,
+                                    uint16_t* aStype)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  return surrogate->NPP_NewStream(aType, aStream, aSeekable, aStype);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_SetWindow(NPP aInstance, NPWindow* aWindow)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  return surrogate->NPP_SetWindow(aWindow);
+}
+
+/* static */ void
+PluginAsyncSurrogate::NPP_Print(NPP aInstance, NPPrint* aPrintInfo)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  surrogate->NPP_Print(aPrintInfo);
+}
+
+/* static */ int16_t
+PluginAsyncSurrogate::NPP_HandleEvent(NPP aInstance, void* aEvent)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  return surrogate->NPP_HandleEvent(aEvent);
+}
+
+/* static */ int32_t
+PluginAsyncSurrogate::NPP_WriteReady(NPP aInstance, NPStream* aStream)
+{
+  PluginAsyncSurrogate* surrogate = Cast(aInstance);
+  MOZ_ASSERT(surrogate);
+  return surrogate->NPP_WriteReady(aStream);
+}
+
+PluginAsyncSurrogate::PendingNewStreamCall::PendingNewStreamCall(
+    NPMIMEType aType, NPStream* aStream, NPBool aSeekable)
+  : mType(NullableString(aType))
+  , mStream(aStream)
+  , mSeekable(aSeekable)
+{
+}
+
+/* static */ bool
+PluginAsyncSurrogate::SetStreamType(NPStream* aStream, uint16_t aStreamType)
+{
+  nsNPAPIStreamWrapper* wrapper =
+    reinterpret_cast<nsNPAPIStreamWrapper*>(aStream->ndata);
+  if (!wrapper) {
+    return false;
+  }
+  nsNPAPIPluginStreamListener* streamListener = wrapper->GetStreamListener();
+  if (!streamListener) {
+    return false;
+  }
+  return streamListener->SetStreamType(aStreamType);
+}
+
+void
+PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance)
+{
+  for (PRUint32 i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
+    PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
+    uint16_t streamType = NP_NORMAL;
+    NPError curError = aInstance->NPP_NewStream(
+                    const_cast<char*>(NullableStringGet(curPendingCall.mType)),
+                    curPendingCall.mStream, curPendingCall.mSeekable,
+                    &streamType);
+    if (curError != NPERR_NO_ERROR) {
+      // If we failed here then the send failed and we need to clean up
+      parent::_destroystream(mInstance, curPendingCall.mStream, NPRES_DONE);
+    }
+  }
+  mPendingNewStreamCalls.Clear();
+  mInstantiated = true;
+}
+
+/**
+ * During asynchronous initialization it might be necessary to wait for the
+ * plugin to complete its initialization. This typically occurs when the result
+ * of a plugin call depends on the plugin being fully instantiated. For example,
+ * if some JS calls into the plugin, the call must be executed synchronously to
+ * preserve correctness.
+ *
+ * This function works by pumping the plugin's IPC channel for events until
+ * initialization has completed.
+ */
+bool
+PluginAsyncSurrogate::WaitForInit()
+{
+  if (mInitCancelled) {
+    return false;
+  }
+  if (mAcceptCalls) {
+    return true;
+  }
+  Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGINASYNCSURROGATE_WAITFORINIT_MS>
+    timer(mParent->GetHistogramKey());
+  bool result = false;
+  MOZ_ASSERT(mParent);
+  if (mParent->IsChrome()) {
+    PluginProcessParent* process = static_cast<PluginModuleChromeParent*>(mParent)->Process();
+    MOZ_ASSERT(process);
+    process->SetCallRunnableImmediately(true);
+    if (!process->WaitUntilConnected()) {
+      return false;
+    }
+  }
+  if (!mParent->WaitForIPCConnection()) {
+    return false;
+  }
+  if (!mParent->IsChrome()) {
+    // For e10s content processes, we need to spin the content channel until the
+    // protocol bridging has occurred.
+    dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+    mozilla::ipc::MessageChannel* contentChannel = cp->GetIPCChannel();
+    MOZ_ASSERT(contentChannel);
+    while (!mParent->mNPInitialized) {
+      result = contentChannel->WaitForIncomingMessage();
+      if (!result) {
+        return result;
+      }
+    }
+  }
+  mozilla::ipc::MessageChannel* channel = mParent->GetIPCChannel();
+  MOZ_ASSERT(channel);
+  while (!mAcceptCalls) {
+    result = channel->WaitForIncomingMessage();
+    if (!result) {
+      break;
+    }
+  }
+  return result;
+}
+
+void
+PluginAsyncSurrogate::AsyncCallDeparting()
+{
+  ++mAsyncCallsInFlight;
+  if (!mPluginDestructionGuard) {
+    mPluginDestructionGuard = MakeUnique<PluginDestructionGuard>(this);
+  }
+}
+
+void
+PluginAsyncSurrogate::AsyncCallArriving()
+{
+  MOZ_ASSERT(mAsyncCallsInFlight > 0);
+  if (--mAsyncCallsInFlight == 0) {
+    mPluginDestructionGuard.reset(nullptr);
+  }
+}
+
+void
+PluginAsyncSurrogate::NotifyAsyncInitFailed()
+{
+  // Clean up any pending NewStream requests
+  for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
+    PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
+    parent::_destroystream(mInstance, curPendingCall.mStream, NPRES_DONE);
+  }
+  mPendingNewStreamCalls.Clear();
+
+  nsNPAPIPluginInstance* inst =
+    static_cast<nsNPAPIPluginInstance*>(mInstance->ndata);
+  if (!inst) {
+      return;
+  }
+  nsPluginInstanceOwner* owner = inst->GetOwner();
+  if (!owner) {
+      return;
+  }
+  owner->NotifyHostAsyncInitFailed();
+}
+
+// static
+NPObject*
+PluginAsyncSurrogate::ScriptableAllocate(NPP aInstance, NPClass* aClass)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aClass != GetClass()) {
+    NS_ERROR("Huh?! Wrong class!");
+    return nullptr;
+  }
+
+  return new AsyncNPObject(Cast(aInstance));
+}
+
+// static
+void
+PluginAsyncSurrogate::ScriptableInvalidate(NPObject* aObject)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return;
+  }
+  realObject->_class->invalidate(realObject);
+}
+
+// static
+void
+PluginAsyncSurrogate::ScriptableDeallocate(NPObject* aObject)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  delete object;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableHasMethod(NPObject* aObject,
+                                                  NPIdentifier aName)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  RecursionGuard guard;
+  if (guard.IsRecursive()) {
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  MOZ_ASSERT(object);
+  bool checkPluginObject = !object->mSurrogate->mInstantiated &&
+                           !object->mSurrogate->mAcceptCalls;
+
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  bool result = realObject->_class->hasMethod(realObject, aName);
+  if (!result && checkPluginObject) {
+    // We may be calling into this object because properties in the WebIDL
+    // object hadn't been set yet. Now that we're further along in
+    // initialization, we should try again.
+    const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
+    NPObject* pluginObject = nullptr;
+    NPError nperror = npn->getvalue(object->mSurrogate->mInstance,
+                                    NPNVPluginElementNPObject,
+                                    (void*)&pluginObject);
+    if (nperror == NPERR_NO_ERROR) {
+      NPPAutoPusher nppPusher(object->mSurrogate->mInstance);
+      result = pluginObject->_class->hasMethod(pluginObject, aName);
+      npn->releaseobject(pluginObject);
+      NPUTF8* idstr = npn->utf8fromidentifier(aName);
+      npn->memfree(idstr);
+    }
+  }
+  return result;
+}
+
+bool
+PluginAsyncSurrogate::GetPropertyHelper(NPObject* aObject, NPIdentifier aName,
+                                        bool* aHasProperty, bool* aHasMethod,
+                                        NPVariant* aResult)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+
+  if (!aObject) {
+    return false;
+  }
+
+  RecursionGuard guard;
+  if (guard.IsRecursive()) {
+    return false;
+  }
+
+  WaitForInit();
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  NPObject* realObject = object->GetRealObject();
+  if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  PluginScriptableObjectParent* actor =
+    static_cast<ParentNPObject*>(realObject)->parent;
+  bool success = actor->GetPropertyHelper(aName, aHasProperty, aHasMethod, aResult);
+  if (!success) {
+    const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
+    NPObject* pluginObject = nullptr;
+    NPError nperror = npn->getvalue(mInstance, NPNVPluginElementNPObject,
+                                    (void*)&pluginObject);
+    if (nperror == NPERR_NO_ERROR) {
+      NPPAutoPusher nppPusher(mInstance);
+      bool hasProperty = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
+      NPUTF8* idstr = npn->utf8fromidentifier(aName);
+      npn->memfree(idstr);
+      bool hasMethod = false;
+      if (hasProperty) {
+        hasMethod = pluginObject->_class->hasMethod(pluginObject, aName);
+        success = pluginObject->_class->getProperty(pluginObject, aName, aResult);
+        idstr = npn->utf8fromidentifier(aName);
+        npn->memfree(idstr);
+      }
+      *aHasProperty = hasProperty;
+      *aHasMethod = hasMethod;
+      npn->releaseobject(pluginObject);
+    }
+  }
+  return success;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableInvoke(NPObject* aObject,
+                                               NPIdentifier aName,
+                                               const NPVariant* aArgs,
+                                               uint32_t aArgCount,
+                                               NPVariant* aResult)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  return realObject->_class->invoke(realObject, aName, aArgs, aArgCount, aResult);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableInvokeDefault(NPObject* aObject,
+                                                      const NPVariant* aArgs,
+                                                      uint32_t aArgCount,
+                                                      NPVariant* aResult)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  return realObject->_class->invokeDefault(realObject, aArgs, aArgCount, aResult);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableHasProperty(NPObject* aObject,
+                                                    NPIdentifier aName)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  RecursionGuard guard;
+  if (guard.IsRecursive()) {
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  MOZ_ASSERT(object);
+  bool checkPluginObject = !object->mSurrogate->mInstantiated &&
+                           !object->mSurrogate->mAcceptCalls;
+
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  bool result = realObject->_class->hasProperty(realObject, aName);
+  const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
+  NPUTF8* idstr = npn->utf8fromidentifier(aName);
+  npn->memfree(idstr);
+  if (!result && checkPluginObject) {
+    // We may be calling into this object because properties in the WebIDL
+    // object hadn't been set yet. Now that we're further along in
+    // initialization, we should try again.
+    NPObject* pluginObject = nullptr;
+    NPError nperror = npn->getvalue(object->mSurrogate->mInstance,
+                                    NPNVPluginElementNPObject,
+                                    (void*)&pluginObject);
+    if (nperror == NPERR_NO_ERROR) {
+      NPPAutoPusher nppPusher(object->mSurrogate->mInstance);
+      result = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
+      npn->releaseobject(pluginObject);
+      idstr = npn->utf8fromidentifier(aName);
+      npn->memfree(idstr);
+    }
+  }
+  return result;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableGetProperty(NPObject* aObject,
+                                                    NPIdentifier aName,
+                                                    NPVariant* aResult)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  // See GetPropertyHelper below.
+  NS_NOTREACHED("Shouldn't ever call this directly!");
+  return false;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableSetProperty(NPObject* aObject,
+                                                    NPIdentifier aName,
+                                                    const NPVariant* aValue)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  return realObject->_class->setProperty(realObject, aName, aValue);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableRemoveProperty(NPObject* aObject,
+                                                       NPIdentifier aName)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  return realObject->_class->removeProperty(realObject, aName);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableEnumerate(NPObject* aObject,
+                                                  NPIdentifier** aIdentifiers,
+                                                  uint32_t* aCount)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  return realObject->_class->enumerate(realObject, aIdentifiers, aCount);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableConstruct(NPObject* aObject,
+                                                  const NPVariant* aArgs,
+                                                  uint32_t aArgCount,
+                                                  NPVariant* aResult)
+{
+  PLUGIN_LOG_DEBUG_FUNCTION;
+  if (aObject->_class != GetClass()) {
+    NS_ERROR("Don't know what kind of object this is!");
+    return false;
+  }
+
+  AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+  if (!object->mSurrogate->WaitForInit()) {
+    return false;
+  }
+  NPObject* realObject = object->GetRealObject();
+  if (!realObject) {
+    return false;
+  }
+  return realObject->_class->construct(realObject, aArgs, aArgCount, aResult);
+}
+
+const NPClass PluginAsyncSurrogate::sNPClass = {
+  NP_CLASS_STRUCT_VERSION,
+  PluginAsyncSurrogate::ScriptableAllocate,
+  PluginAsyncSurrogate::ScriptableDeallocate,
+  PluginAsyncSurrogate::ScriptableInvalidate,
+  PluginAsyncSurrogate::ScriptableHasMethod,
+  PluginAsyncSurrogate::ScriptableInvoke,
+  PluginAsyncSurrogate::ScriptableInvokeDefault,
+  PluginAsyncSurrogate::ScriptableHasProperty,
+  PluginAsyncSurrogate::ScriptableGetProperty,
+  PluginAsyncSurrogate::ScriptableSetProperty,
+  PluginAsyncSurrogate::ScriptableRemoveProperty,
+  PluginAsyncSurrogate::ScriptableEnumerate,
+  PluginAsyncSurrogate::ScriptableConstruct
+};
+
+PushSurrogateAcceptCalls::PushSurrogateAcceptCalls(PluginInstanceParent* aInstance)
+  : mSurrogate(nullptr)
+  , mPrevAcceptCallsState(false)
+{
+  MOZ_ASSERT(aInstance);
+  mSurrogate = aInstance->GetAsyncSurrogate();
+  if (mSurrogate) {
+    mPrevAcceptCallsState = mSurrogate->SetAcceptingCalls(true);
+  }
+}
+
+PushSurrogateAcceptCalls::~PushSurrogateAcceptCalls()
+{
+  if (mSurrogate) {
+    mSurrogate->SetAcceptingCalls(mPrevAcceptCallsState);
+  }
+}
+
+} // namespace plugins
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginAsyncSurrogate.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef dom_plugins_ipc_PluginAsyncSurrogate_h
+#define dom_plugins_ipc_PluginAsyncSurrogate_h
+
+#include "mozilla/UniquePtr.h"
+#include "npapi.h"
+#include "npfunctions.h"
+#include "npruntime.h"
+#include "nsAutoPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsPluginHost.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "PluginDataResolver.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceParent;
+class PluginModuleParent;
+
+class PluginAsyncSurrogate : public PluginDataResolver
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(PluginAsyncSurrogate)
+
+  bool Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode,
+            int16_t aArgc, char* aArgn[], char* aArgv[]);
+  nsresult NPP_New(NPError* aError);
+  NPError NPP_Destroy(NPSavedData** aSave);
+  NPError NPP_GetValue(NPPVariable aVariable, void* aRetval);
+  NPError NPP_SetValue(NPNVariable aVariable, void* aValue);
+  NPError NPP_NewStream(NPMIMEType aType, NPStream* aStream, NPBool aSeekable,
+                        uint16_t* aStype);
+  NPError NPP_SetWindow(NPWindow* aWindow);
+  nsresult AsyncSetWindow(NPWindow* aWindow);
+  void NPP_Print(NPPrint* aPrintInfo);
+  int16_t NPP_HandleEvent(void* aEvent);
+  int32_t NPP_WriteReady(NPStream* aStream);
+  void OnInstanceCreated(PluginInstanceParent* aInstance);
+  static bool Create(PluginModuleParent* aParent, NPMIMEType aPluginType,
+                     NPP aInstance, uint16_t aMode, int16_t aArgc,
+                     char* aArgn[], char* aArgv[]);
+  static const NPClass* GetClass() { return &sNPClass; }
+  static void NP_GetEntryPoints(NPPluginFuncs* aFuncs);
+  static PluginAsyncSurrogate* Cast(NPP aInstance);
+
+  virtual PluginAsyncSurrogate*
+  GetAsyncSurrogate() { return this; }
+
+  virtual PluginInstanceParent*
+  GetInstance() { return nullptr; }
+
+  NPP GetNPP() { return mInstance; }
+
+  bool GetPropertyHelper(NPObject* aObject, NPIdentifier aName,
+                         bool* aHasProperty, bool* aHasMethod,
+                         NPVariant* aResult);
+
+  PluginModuleParent*
+  GetParent() { return mParent; }
+
+  bool SetAcceptingCalls(bool aAccept)
+  {
+    bool prevState = mAcceptCalls;
+    if (mInstantiated) {
+      aAccept = true;
+    }
+    mAcceptCalls = aAccept;
+    return prevState;
+  }
+
+  void AsyncCallDeparting();
+  void AsyncCallArriving();
+
+  void NotifyAsyncInitFailed();
+
+private:
+  explicit PluginAsyncSurrogate(PluginModuleParent* aParent);
+  virtual ~PluginAsyncSurrogate();
+
+  bool WaitForInit();
+
+  static bool SetStreamType(NPStream* aStream, uint16_t aStreamType);
+
+  static NPError NPP_Destroy(NPP aInstance, NPSavedData** aSave);
+  static NPError NPP_GetValue(NPP aInstance, NPPVariable aVariable, void* aRetval);
+  static NPError NPP_SetValue(NPP aInstance, NPNVariable aVariable, void* aValue);
+  static NPError NPP_NewStream(NPP aInstance, NPMIMEType aType, NPStream* aStream,
+                               NPBool aSeekable, uint16_t* aStype);
+  static NPError NPP_SetWindow(NPP aInstance, NPWindow* aWindow);
+  static void NPP_Print(NPP aInstance, NPPrint* aPrintInfo);
+  static int16_t NPP_HandleEvent(NPP aInstance, void* aEvent);
+  static int32_t NPP_WriteReady(NPP aInstance, NPStream* aStream);
+
+  static NPObject* ScriptableAllocate(NPP aInstance, NPClass* aClass);
+  static void ScriptableInvalidate(NPObject* aObject);
+  static void ScriptableDeallocate(NPObject* aObject);
+  static bool ScriptableHasMethod(NPObject* aObject, NPIdentifier aName);
+  static bool ScriptableInvoke(NPObject* aObject, NPIdentifier aName,
+                               const NPVariant* aArgs, uint32_t aArgCount,
+                               NPVariant* aResult);
+  static bool ScriptableInvokeDefault(NPObject* aObject, const NPVariant* aArgs,
+                                      uint32_t aArgCount, NPVariant* aResult);
+  static bool ScriptableHasProperty(NPObject* aObject, NPIdentifier aName);
+  static bool ScriptableGetProperty(NPObject* aObject, NPIdentifier aName,
+                                    NPVariant* aResult);
+  static bool ScriptableSetProperty(NPObject* aObject, NPIdentifier aName,
+                                    const NPVariant* aValue);
+  static bool ScriptableRemoveProperty(NPObject* aObject, NPIdentifier aName);
+  static bool ScriptableEnumerate(NPObject* aObject, NPIdentifier** aIdentifiers,
+                                  uint32_t* aCount);
+  static bool ScriptableConstruct(NPObject* aObject, const NPVariant* aArgs,
+                                  uint32_t aArgCount, NPVariant* aResult);
+
+private:
+  struct PendingNewStreamCall
+  {
+    PendingNewStreamCall(NPMIMEType aType, NPStream* aStream, NPBool aSeekable);
+    ~PendingNewStreamCall() {}
+    nsCString   mType;
+    NPStream*   mStream;
+    NPBool      mSeekable;
+  };
+
+private:
+  PluginModuleParent*             mParent;
+  // These values are used to construct the plugin instance
+  nsCString                       mMimeType;
+  NPP                             mInstance;
+  uint16_t                        mMode;
+  InfallibleTArray<nsCString>     mNames;
+  InfallibleTArray<nsCString>     mValues;
+  // This is safe to store as a pointer because the spec says it will remain
+  // valid until destruction or a subsequent NPP_SetWindow call.
+  NPWindow*                       mWindow;
+  nsTArray<PendingNewStreamCall>  mPendingNewStreamCalls;
+  UniquePtr<PluginDestructionGuard> mPluginDestructionGuard;
+
+  bool      mAcceptCalls;
+  bool      mInstantiated;
+  bool      mAsyncSetWindow;
+  bool      mInitCancelled;
+  int32_t   mAsyncCallsInFlight;
+
+  static const NPClass sNPClass;
+};
+
+struct AsyncNPObject : NPObject
+{
+  explicit AsyncNPObject(PluginAsyncSurrogate* aSurrogate);
+  ~AsyncNPObject();
+
+  NPObject* GetRealObject();
+
+  nsRefPtr<PluginAsyncSurrogate>  mSurrogate;
+  NPObject*                       mRealObject;
+};
+
+class MOZ_STACK_CLASS PushSurrogateAcceptCalls
+{
+public:
+  explicit PushSurrogateAcceptCalls(PluginInstanceParent* aInstance);
+  ~PushSurrogateAcceptCalls();
+
+private:
+  PluginAsyncSurrogate* mSurrogate;
+  bool                  mPrevAcceptCallsState;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_ipc_PluginAsyncSurrogate_h
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginDataResolver.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef dom_plugins_ipc_PluginDataResolver_h
+#define dom_plugins_ipc_PluginDataResolver_h
+
+namespace mozilla {
+namespace plugins {
+
+class PluginAsyncSurrogate;
+class PluginInstanceParent;
+
+class PluginDataResolver
+{
+public:
+    virtual PluginAsyncSurrogate* GetAsyncSurrogate() = 0;
+    virtual PluginInstanceParent* GetInstance() = 0;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_ipc_PluginDataResolver_h
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -114,18 +114,26 @@ CreateDrawTargetForSurface(gfxASurface *
   RefPtr<DrawTarget> drawTarget =
     Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(),
                                              ToIntSize(gfxIntSize(aSurface->GetSize())),
                                              &format);
   aSurface->SetData(&kDrawTarget, drawTarget, nullptr);
   return drawTarget;
 }
 
-PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface)
+PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+                                         const nsCString& aMimeType,
+                                         const uint16_t& aMode,
+                                         const InfallibleTArray<nsCString>& aNames,
+                                         const InfallibleTArray<nsCString>& aValues)
     : mPluginIface(aPluginIface)
+    , mMimeType(aMimeType)
+    , mMode(aMode)
+    , mNames(aNames)
+    , mValues(aValues)
 #if defined(XP_MACOSX)
     , mContentsScaleFactor(1.0)
 #endif
     , mDrawingModel(kDefaultDrawingModel)
     , mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex")
     , mAsyncInvalidateTask(0)
     , mCachedWindowActor(nullptr)
     , mCachedElementActor(nullptr)
@@ -205,16 +213,64 @@ PluginInstanceChild::~PluginInstanceChil
         PluginUtilsOSX::ReleaseCGLayer(mCGLayer);
     }
     if (mDrawingModel == NPDrawingModelCoreAnimation) {
         UnscheduleTimer(mCARefreshTimer);
     }
 #endif
 }
 
+NPError
+PluginInstanceChild::DoNPP_New()
+{
+    // unpack the arguments into a C format
+    int argc = mNames.Length();
+    NS_ASSERTION(argc == (int) mValues.Length(),
+                 "argn.length != argv.length");
+
+    nsAutoArrayPtr<char*> argn(new char*[1 + argc]);
+    nsAutoArrayPtr<char*> argv(new char*[1 + argc]);
+    argn[argc] = 0;
+    argv[argc] = 0;
+
+    for (int i = 0; i < argc; ++i) {
+        argn[i] = const_cast<char*>(NullableStringGet(mNames[i]));
+        argv[i] = const_cast<char*>(NullableStringGet(mValues[i]));
+    }
+
+    NPP npp = GetNPP();
+
+    NPError rv = mPluginIface->newp((char*)NullableStringGet(mMimeType), npp,
+                                    mMode, argc, argn, argv, 0);
+    if (NPERR_NO_ERROR != rv) {
+        return rv;
+    }
+
+    Initialize();
+
+#if defined(XP_MACOSX) && defined(__i386__)
+    // If an i386 Mac OS X plugin has selected the Carbon event model then
+    // we have to fail. We do not support putting Carbon event model plugins
+    // out of process. Note that Carbon is the default model so out of process
+    // plugins need to actively negotiate something else in order to work
+    // out of process.
+    if (EventModel() == NPEventModelCarbon) {
+      // Send notification that a plugin tried to negotiate Carbon NPAPI so that
+      // users can be notified that restarting the browser in i386 mode may allow
+      // them to use the plugin.
+      SendNegotiatedCarbon();
+
+      // Fail to instantiate.
+      rv = NPERR_MODULE_LOAD_FAILED_ERROR;
+    }
+#endif
+
+    return rv;
+}
+
 int
 PluginInstanceChild::GetQuirks()
 {
     return PluginModuleChild::GetChrome()->GetQuirks();
 }
 
 NPError
 PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
@@ -2229,49 +2285,110 @@ PluginInstanceChild::RecvPPluginScriptab
 
     actor->InitializeProxy();
     NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
 
     return true;
 }
 
 bool
-PluginInstanceChild::AnswerPBrowserStreamConstructor(
+PluginInstanceChild::RecvPBrowserStreamConstructor(
     PBrowserStreamChild* aActor,
     const nsCString& url,
     const uint32_t& length,
     const uint32_t& lastmodified,
     PStreamNotifyChild* notifyData,
-    const nsCString& headers,
-    const nsCString& mimeType,
-    const bool& seekable,
-    NPError* rv,
-    uint16_t* stype)
+    const nsCString& headers)
+{
+    return true;
+}
+
+NPError
+PluginInstanceChild::DoNPP_NewStream(BrowserStreamChild* actor,
+                                     const nsCString& mimeType,
+                                     const bool& seekable,
+                                     uint16_t* stype)
 {
     AssertPluginThread();
-    *rv = static_cast<BrowserStreamChild*>(aActor)
-          ->StreamConstructed(mimeType, seekable, stype);
+    NPError rv = actor->StreamConstructed(mimeType, seekable, stype);
+    return rv;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_NewStream(PBrowserStreamChild* actor,
+                                         const nsCString& mimeType,
+                                         const bool& seekable,
+                                         NPError* rv,
+                                         uint16_t* stype)
+{
+    *rv = DoNPP_NewStream(static_cast<BrowserStreamChild*>(actor), mimeType,
+                          seekable, stype);
+    return true;
+}
+
+class NewStreamAsyncCall : public ChildAsyncCall
+{
+public:
+    NewStreamAsyncCall(PluginInstanceChild* aInstance,
+                       BrowserStreamChild* aBrowserStreamChild,
+                       const nsCString& aMimeType,
+                       const bool aSeekable)
+        : ChildAsyncCall(aInstance, nullptr, nullptr)
+        , mBrowserStreamChild(aBrowserStreamChild)
+        , mMimeType(aMimeType)
+        , mSeekable(aSeekable)
+    {
+    }
+
+    void Run() MOZ_OVERRIDE
+    {
+        RemoveFromAsyncList();
+
+        uint16_t stype = NP_NORMAL;
+        NPError rv = mInstance->DoNPP_NewStream(mBrowserStreamChild, mMimeType,
+                                                mSeekable, &stype);
+        DebugOnly<bool> sendOk =
+            mBrowserStreamChild->SendAsyncNPP_NewStreamResult(rv, stype);
+        MOZ_ASSERT(sendOk);
+    }
+
+private:
+    BrowserStreamChild* mBrowserStreamChild;
+    const nsCString     mMimeType;
+    const bool          mSeekable;
+};
+
+bool
+PluginInstanceChild::RecvAsyncNPP_NewStream(PBrowserStreamChild* actor,
+                                            const nsCString& mimeType,
+                                            const bool& seekable)
+{
+    // Reusing ChildAsyncCall so that the task is cancelled properly on Destroy
+    BrowserStreamChild* child = static_cast<BrowserStreamChild*>(actor);
+    NewStreamAsyncCall* task = new NewStreamAsyncCall(this, child, mimeType,
+                                                      seekable);
+    {
+        MutexAutoLock lock(mAsyncCallMutex);
+        mPendingAsyncCalls.AppendElement(task);
+    }
+    MessageLoop::current()->PostTask(FROM_HERE, task);
     return true;
 }
 
 PBrowserStreamChild*
 PluginInstanceChild::AllocPBrowserStreamChild(const nsCString& url,
                                               const uint32_t& length,
                                               const uint32_t& lastmodified,
                                               PStreamNotifyChild* notifyData,
-                                              const nsCString& headers,
-                                              const nsCString& mimeType,
-                                              const bool& seekable,
-                                              NPError* rv,
-                                              uint16_t *stype)
+                                              const nsCString& headers)
 {
     AssertPluginThread();
     return new BrowserStreamChild(this, url, length, lastmodified,
                                   static_cast<StreamNotifyChild*>(notifyData),
-                                  headers, mimeType, seekable, rv, stype);
+                                  headers);
 }
 
 bool
 PluginInstanceChild::DeallocPBrowserStreamChild(PBrowserStreamChild* stream)
 {
     AssertPluginThread();
     delete stream;
     return true;
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -138,39 +138,41 @@ protected:
     AllocPPluginScriptableObjectChild() MOZ_OVERRIDE;
 
     virtual bool
     DeallocPPluginScriptableObjectChild(PPluginScriptableObjectChild* aObject) MOZ_OVERRIDE;
 
     virtual bool
     RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectChild* aActor) MOZ_OVERRIDE;
 
+    virtual bool
+    RecvPBrowserStreamConstructor(PBrowserStreamChild* aActor, const nsCString& url,
+                                  const uint32_t& length, const uint32_t& lastmodified,
+                                  PStreamNotifyChild* notifyData, const nsCString& headers);
+
+    virtual bool
+    AnswerNPP_NewStream(
+            PBrowserStreamChild* actor,
+            const nsCString& mimeType,
+            const bool& seekable,
+            NPError* rv,
+            uint16_t* stype) MOZ_OVERRIDE;
+
+    virtual bool
+    RecvAsyncNPP_NewStream(
+            PBrowserStreamChild* actor,
+            const nsCString& mimeType,
+            const bool& seekable) MOZ_OVERRIDE;
+
     virtual PBrowserStreamChild*
     AllocPBrowserStreamChild(const nsCString& url,
                              const uint32_t& length,
                              const uint32_t& lastmodified,
                              PStreamNotifyChild* notifyData,
-                             const nsCString& headers,
-                             const nsCString& mimeType,
-                             const bool& seekable,
-                             NPError* rv,
-                             uint16_t *stype) MOZ_OVERRIDE;
-
-    virtual bool
-    AnswerPBrowserStreamConstructor(
-            PBrowserStreamChild* aActor,
-            const nsCString& url,
-            const uint32_t& length,
-            const uint32_t& lastmodified,
-            PStreamNotifyChild* notifyData,
-            const nsCString& headers,
-            const nsCString& mimeType,
-            const bool& seekable,
-            NPError* rv,
-            uint16_t* stype) MOZ_OVERRIDE;
+                             const nsCString& headers) MOZ_OVERRIDE;
 
     virtual bool
     DeallocPBrowserStreamChild(PBrowserStreamChild* stream) MOZ_OVERRIDE;
 
     virtual PPluginStreamChild*
     AllocPPluginStreamChild(const nsCString& mimeType,
                             const nsCString& target,
                             NPError* result) MOZ_OVERRIDE;
@@ -197,20 +199,32 @@ protected:
     RecvNPP_DidComposite() MOZ_OVERRIDE;
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     bool CreateWindow(const NPRemoteWindow& aWindow);
     void DeleteWindow();
 #endif
 
 public:
-    explicit PluginInstanceChild(const NPPluginFuncs* aPluginIface);
+    PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+                        const nsCString& aMimeType,
+                        const uint16_t& aMode,
+                        const InfallibleTArray<nsCString>& aNames,
+                        const InfallibleTArray<nsCString>& aValues);
 
     virtual ~PluginInstanceChild();
 
+    NPError DoNPP_New();
+
+    // Common sync+async implementation of NPP_NewStream
+    NPError DoNPP_NewStream(BrowserStreamChild* actor,
+                            const nsCString& mimeType,
+                            const bool& seekable,
+                            uint16_t* stype);
+
     bool Initialize();
 
     NPP GetNPP()
     {
         return &mData;
     }
 
     NPError
@@ -347,16 +361,20 @@ private:
         UINT                 mMsg;
         WPARAM               mWParam;
         LPARAM               mLParam;
         bool                 mWindowed;
     };
 
 #endif
     const NPPluginFuncs* mPluginIface;
+    nsCString                   mMimeType;
+    uint16_t                    mMode;
+    InfallibleTArray<nsCString> mNames;
+    InfallibleTArray<nsCString> mValues;
     NPP_t mData;
     NPWindow mWindow;
 #if defined(XP_MACOSX)
     double mContentsScaleFactor;
 #endif
     int16_t               mDrawingModel;
 
     mozilla::Mutex mAsyncInvalidateMutex;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -5,27 +5,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 #include <stdint.h> // for intptr_t
 
 #include "mozilla/Telemetry.h"
 #include "PluginInstanceParent.h"
 #include "BrowserStreamParent.h"
+#include "PluginAsyncSurrogate.h"
 #include "PluginBackgroundDestroyer.h"
 #include "PluginModuleParent.h"
 #include "PluginStreamParent.h"
 #include "StreamNotifyParent.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "gfxASurface.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxSharedImageSurface.h"
 #include "nsNPAPIPluginInstance.h"
+#include "nsPluginInstanceOwner.h"
 #ifdef MOZ_X11
 #include "gfxXlibSurface.h"
 #endif
 #include "gfxContext.h"
 #include "gfxColor.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "Layers.h"
@@ -70,17 +72,19 @@ StreamNotifyParent::RecvRedirectNotifyRe
   instance->mNPNIface->urlredirectresponse(instance->mNPP, this, static_cast<NPBool>(allow));
   return true;
 }
 
 PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
                                            NPP npp,
                                            const nsCString& aMimeType,
                                            const NPNetscapeFuncs* npniface)
-  : mParent(parent)
+    : mParent(parent)
+    , mSurrogate(PluginAsyncSurrogate::Cast(npp))
+    , mUseSurrogate(true)
     , mNPP(npp)
     , mNPNIface(npniface)
     , mWindowType(NPWindowTypeWindow)
     , mDrawingModel(kDefaultDrawingModel)
 #if defined(OS_WIN)
     , mPluginHWND(nullptr)
     , mPluginWndProc(nullptr)
     , mNestedEventState(false)
@@ -162,21 +166,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)
 {
@@ -778,16 +778,22 @@ PluginInstanceParent::EndUpdateBackgroun
     XSync(DefaultXDisplay(), False);
 #endif
 
     unused << SendUpdateBackground(BackgroundDescriptor(), aRect);
 
     return NS_OK;
 }
 
+PluginAsyncSurrogate*
+PluginInstanceParent::GetAsyncSurrogate()
+{
+    return mSurrogate;
+}
+
 bool
 PluginInstanceParent::CreateBackground(const nsIntSize& aSize)
 {
     NS_ABORT_IF_FALSE(!mBackground, "Already have a background");
 
     // XXX refactor me
 
 #if defined(MOZ_X11)
@@ -1304,35 +1310,47 @@ NPError
 PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
                                     NPBool seekable, uint16_t* stype)
 {
     PLUGIN_LOG_DEBUG(("%s (type=%s, stream=%p, seekable=%i)",
                       FULLFUNCTION, (char*) type, (void*) stream, (int) seekable));
 
     BrowserStreamParent* bs = new BrowserStreamParent(this, stream);
 
-    NPError err;
-    {   // Scope for timer
-        Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_STREAM_INIT_MS>
-            timer(Module()->GetHistogramKey());
-        if (!CallPBrowserStreamConstructor(bs,
-                                           NullableString(stream->url),
-                                           stream->end,
-                                           stream->lastmodified,
-                                           static_cast<PStreamNotifyParent*>(stream->notifyData),
-                                           NullableString(stream->headers),
-                                           NullableString(type), seekable,
-                                           &err, stype)) {
-            return NPERR_GENERIC_ERROR;
+    if (!SendPBrowserStreamConstructor(bs,
+                                       NullableString(stream->url),
+                                       stream->end,
+                                       stream->lastmodified,
+                                       static_cast<PStreamNotifyParent*>(stream->notifyData),
+                                       NullableString(stream->headers))) {
+        return NPERR_GENERIC_ERROR;
+    }
+
+    Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_STREAM_INIT_MS>
+        timer(Module()->GetHistogramKey());
+
+    NPError err = NPERR_NO_ERROR;
+    if (mParent->IsStartingAsync()) {
+        MOZ_ASSERT(mSurrogate);
+        mSurrogate->AsyncCallDeparting();
+        if (SendAsyncNPP_NewStream(bs, NullableString(type), seekable)) {
+            *stype = UINT16_MAX;
+        } else {
+            err = NPERR_GENERIC_ERROR;
+        }
+    } else {
+        bs->SetAlive();
+        if (!CallNPP_NewStream(bs, NullableString(type), seekable, &err, stype)) {
+            err = NPERR_GENERIC_ERROR;
+        }
+        if (NPERR_NO_ERROR != err) {
+            unused << PBrowserStreamParent::Send__delete__(bs);
         }
     }
 
-    if (NPERR_NO_ERROR != err)
-        unused << PBrowserStreamParent::Send__delete__(bs);
-
     return err;
 }
 
 NPError
 PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason)
 {
     PLUGIN_LOG_DEBUG(("%s (stream=%p, reason=%i)",
                       FULLFUNCTION, (void*) stream, (int) reason));
@@ -1631,16 +1649,59 @@ PluginInstanceParent::RecvNegotiatedCarb
     nsNPAPIPluginInstance *inst = static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
     if (!inst) {
         return false;
     }
     inst->CarbonNPAPIFailure();
     return true;
 }
 
+nsPluginInstanceOwner*
+PluginInstanceParent::GetOwner()
+{
+    nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
+    if (!inst) {
+        return nullptr;
+    }
+    return inst->GetOwner();
+}
+
+bool
+PluginInstanceParent::RecvAsyncNPP_NewResult(const NPError& aResult)
+{
+    // NB: mUseSurrogate must be cleared before doing anything else, especially
+    //     calling NPP_SetWindow!
+    mUseSurrogate = false;
+
+    mSurrogate->AsyncCallArriving();
+    if (aResult == NPERR_NO_ERROR) {
+        mSurrogate->SetAcceptingCalls(true);
+    }
+
+    nsPluginInstanceOwner* owner = GetOwner();
+    if (!owner) {
+        // This is possible in async plugin land; the instance may outlive
+        // the owner
+        return true;
+    }
+
+    if (aResult != NPERR_NO_ERROR) {
+        owner->NotifyHostAsyncInitFailed();
+        return true;
+    }
+
+    // Now we need to do a bunch of exciting post-NPP_New housekeeping.
+    owner->NotifyHostCreateWidget();
+
+    MOZ_ASSERT(mSurrogate);
+    mSurrogate->OnInstanceCreated(this);
+
+    return true;
+}
+
 #if defined(OS_WIN)
 
 /*
   plugin focus changes between processes
 
   focus from dom -> child:
     Focus manager calls on widget to set the focus on the window.
     We pick up the resulting wm_setfocus event here, and forward
@@ -1870,8 +1931,34 @@ PluginInstanceParent::AnswerPluginFocusC
       ::SendMessage(mPluginHWND, gOOPPPluginFocusEvent, gotFocus ? 1 : 0, 0);
     }
     return true;
 #else
     NS_NOTREACHED("PluginInstanceParent::AnswerPluginFocusChange not implemented!");
     return false;
 #endif
 }
+
+PluginInstanceParent*
+PluginInstanceParent::Cast(NPP aInstance, PluginAsyncSurrogate** aSurrogate)
+{
+    PluginDataResolver* resolver =
+        static_cast<PluginDataResolver*>(aInstance->pdata);
+
+    // If the plugin crashed and the PluginInstanceParent was deleted,
+    // aInstance->pdata will be nullptr.
+    if (!resolver) {
+        return nullptr;
+    }
+
+    PluginInstanceParent* instancePtr = resolver->GetInstance();
+
+    if (instancePtr && aInstance != instancePtr->mNPP) {
+        NS_RUNTIMEABORT("Corrupted plugin data.");
+    }
+
+    if (aSurrogate) {
+        *aSurrogate = resolver->GetAsyncSurrogate();
+    }
+
+    return instancePtr;
+}
+
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -17,36 +17,39 @@
 #include "mozilla/gfx/QuartzSupport.h"
 #endif
 
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRect.h"
+#include "PluginDataResolver.h"
 
 #ifdef MOZ_X11
 class gfxXlibSurface;
 #endif
 #include "mozilla/unused.h"
 
 class gfxASurface;
 class gfxContext;
+class nsPluginInstanceOwner;
 
 namespace mozilla {
 namespace layers {
 class ImageContainer;
 class CompositionNotifySink;
 }
 namespace plugins {
 
 class PBrowserStreamParent;
 class PluginModuleParent;
 
 class PluginInstanceParent : public PPluginInstanceParent
+                           , public PluginDataResolver
 {
     friend class PluginModuleParent;
     friend class BrowserStreamParent;
     friend class PluginStreamParent;
     friend class StreamNotifyParent;
 
 public:
     PluginInstanceParent(PluginModuleParent* parent,
@@ -69,21 +72,17 @@ 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
@@ -205,16 +204,19 @@ public:
                            bool *result) MOZ_OVERRIDE;
 
     virtual bool
     RecvRedrawPlugin() MOZ_OVERRIDE;
 
     virtual bool
     RecvNegotiatedCarbon() MOZ_OVERRIDE;
 
+    virtual bool
+    RecvAsyncNPP_NewResult(const NPError& aResult) MOZ_OVERRIDE;
+
     NPError NPP_SetWindow(const NPWindow* aWindow);
 
     NPError NPP_GetValue(NPPVariable variable, void* retval);
     NPError NPP_SetValue(NPNVariable variable, void* value);
 
     void NPP_URLRedirectNotify(const char* url, int32_t status,
                                void* notifyData);
 
@@ -249,16 +251,22 @@ public:
     GetActorForNPObject(NPObject* aObject);
 
     NPP
     GetNPP()
     {
       return mNPP;
     }
 
+    bool
+    UseSurrogate() const
+    {
+        return mUseSurrogate;
+    }
+
     virtual bool
     AnswerPluginFocusChange(const bool& gotFocus) MOZ_OVERRIDE;
 
     nsresult AsyncSetWindow(NPWindow* window);
     nsresult GetImageContainer(mozilla::layers::ImageContainer** aContainer);
     nsresult GetImageSize(nsIntSize* aSize);
 #ifdef XP_MACOSX
     nsresult IsRemoteDrawingCoreAnimation(bool *aDrawing);
@@ -266,16 +274,23 @@ public:
 #endif
     nsresult SetBackgroundUnknown();
     nsresult BeginUpdateBackground(const nsIntRect& aRect,
                                    gfxContext** aCtx);
     nsresult EndUpdateBackground(gfxContext* aCtx,
                                  const nsIntRect& aRect);
     void DidComposite() { unused << SendNPP_DidComposite(); }
 
+    virtual PluginAsyncSurrogate* GetAsyncSurrogate();
+
+    virtual PluginInstanceParent* GetInstance() { return this; }
+
+    static PluginInstanceParent* Cast(NPP instance,
+                                      PluginAsyncSurrogate** aSurrogate = nullptr);
+
 private:
     // Create an appropriate platform surface for a background of size
     // |aSize|.  Return true if successful.
     bool CreateBackground(const nsIntSize& aSize);
     void DestroyBackground();
     SurfaceDescriptor BackgroundDescriptor() /*const*/;
 
     typedef mozilla::layers::ImageContainer ImageContainer;
@@ -286,18 +301,22 @@ private:
 
     virtual bool
     DeallocPPluginBackgroundDestroyerParent(PPluginBackgroundDestroyerParent* aActor) MOZ_OVERRIDE;
 
     bool InternalGetValueForNPObject(NPNVariable aVariable,
                                      PPluginScriptableObjectParent** aValue,
                                      NPError* aResult);
 
+    nsPluginInstanceOwner* GetOwner();
+
 private:
     PluginModuleParent* mParent;
+    nsRefPtr<PluginAsyncSurrogate> mSurrogate;
+    bool mUseSurrogate;
     NPP mNPP;
     const NPNetscapeFuncs* mNPNIface;
     NPWindowType mWindowType;
     int16_t            mDrawingModel;
     nsAutoPtr<mozilla::layers::CompositionNotifySink> mNotifySink;
 
     nsDataHashtable<nsPtrHashKey<NPObject>, PluginScriptableObjectParent*> mScriptableObjects;
 
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -90,16 +90,23 @@ static WCHAR* sReplacementConfigFile;
 static bool gDelayFlashFocusReplyUntilEval = false;
 // Used to fix GetWindowInfo problems with internal flash settings dialogs
 static WindowsDllInterceptor sUser32Intercept;
 typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
 static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
 static HWND sBrowserHwnd = nullptr;
 #endif
 
+template<>
+struct RunnableMethodTraits<PluginModuleChild>
+{
+    static void RetainCallee(PluginModuleChild* obj) { }
+    static void ReleaseCallee(PluginModuleChild* obj) { }
+};
+
 /* static */
 PluginModuleChild*
 PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport,
                                            base::ProcessId aOtherProcess)
 {
     PluginModuleChild* child = new PluginModuleChild(false);
     ProcessHandle handle;
     if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
@@ -1876,17 +1883,31 @@ PluginModuleChild::AnswerNP_GetEntryPoin
     *_retval = mGetEntryPointsFunc(&mFunctions);
     return true;
 #else
 #  error Please implement me for your platform
 #endif
 }
 
 bool
-PluginModuleChild::AnswerNP_Initialize(const PluginSettings& aSettings, NPError* _retval)
+PluginModuleChild::AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv)
+{
+    *rv = DoNP_Initialize(aSettings);
+    return true;
+}
+
+bool
+PluginModuleChild::RecvAsyncNP_Initialize(const PluginSettings& aSettings)
+{
+    NPError error = DoNP_Initialize(aSettings);
+    return SendNP_InitializeResult(error);
+}
+
+NPError
+PluginModuleChild::DoNP_Initialize(const PluginSettings& aSettings)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
     MOZ_ASSERT(mIsChrome);
 
     mCachedSettings = aSettings;
 
 #ifdef OS_WIN
@@ -1895,29 +1916,30 @@ PluginModuleChild::AnswerNP_Initialize(c
 
 #ifdef MOZ_X11
     // Send the parent our X socket to act as a proxy reference for our X
     // resources.
     int xSocketFd = ConnectionNumber(DefaultXDisplay());
     SendBackUpXResources(FileDescriptor(xSocketFd));
 #endif
 
+    NPError result;
 #if defined(OS_LINUX) || defined(OS_BSD)
-    *_retval = mInitializeFunc(&sBrowserFuncs, &mFunctions);
-    return true;
+    result = mInitializeFunc(&sBrowserFuncs, &mFunctions);
 #elif defined(OS_WIN) || defined(OS_MACOSX)
-    *_retval = mInitializeFunc(&sBrowserFuncs);
-    return true;
+    result = mInitializeFunc(&sBrowserFuncs);
 #else
 #  error Please implement me for your platform
 #endif
 
 #ifdef XP_WIN
     CleanupProtectedModeHook();
 #endif
+
+    return result;
 }
 
 #if defined(XP_WIN)
 
 HANDLE WINAPI
 CreateFileHookFn(LPCWSTR fname, DWORD access, DWORD share,
                  LPSECURITY_ATTRIBUTES security, DWORD creation, DWORD flags,
                  HANDLE ftemplate)
@@ -2030,34 +2052,34 @@ PMCGetWindowInfoHook(HWND hWnd, PWINDOWI
   return result;
 }
 #endif
 
 PPluginInstanceChild*
 PluginModuleChild::AllocPPluginInstanceChild(const nsCString& aMimeType,
                                              const uint16_t& aMode,
                                              const InfallibleTArray<nsCString>& aNames,
-                                             const InfallibleTArray<nsCString>& aValues,
-                                             NPError* rv)
+                                             const InfallibleTArray<nsCString>& aValues)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
 
     InitQuirksModes(aMimeType);
 
 #ifdef XP_WIN
     if ((mQuirks & QUIRK_FLASH_HOOK_GETWINDOWINFO) &&
         !sGetWindowInfoPtrStub) {
         sUser32Intercept.Init("user32.dll");
         sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(PMCGetWindowInfoHook),
                                  (void**) &sGetWindowInfoPtrStub);
     }
 #endif
 
-    return new PluginInstanceChild(&mFunctions);
+    return new PluginInstanceChild(&mFunctions, aMimeType, aMode, aNames,
+                                   aValues);
 }
 
 void
 PluginModuleChild::InitQuirksModes(const nsCString& aMimeType)
 {
     if (mQuirks != QUIRKS_NOT_INITIALIZED)
       return;
     mQuirks = 0;
@@ -2100,78 +2122,49 @@ PluginModuleChild::InitQuirksModes(const
     if (FindInReadable(flash, aMimeType) ||
         FindInReadable(quicktime, mPluginFilename)) {
         mQuirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
     }
 #endif
 }
 
 bool
-PluginModuleChild::AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor,
-                                                    const nsCString& aMimeType,
-                                                    const uint16_t& aMode,
-                                                    const InfallibleTArray<nsCString>& aNames,
-                                                    const InfallibleTArray<nsCString>& aValues,
-                                                    NPError* rv)
+PluginModuleChild::RecvPPluginInstanceConstructor(PPluginInstanceChild* aActor,
+                                                  const nsCString& aMimeType,
+                                                  const uint16_t& aMode,
+                                                  const InfallibleTArray<nsCString>& aNames,
+                                                  const InfallibleTArray<nsCString>& aValues)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
 
+    NS_ASSERTION(aActor, "Null actor!");
+    return true;
+}
+
+bool
+PluginModuleChild::AnswerSyncNPP_New(PPluginInstanceChild* aActor, NPError* rv)
+{
+    PLUGIN_LOG_DEBUG_METHOD;
     PluginInstanceChild* childInstance =
         reinterpret_cast<PluginInstanceChild*>(aActor);
-    NS_ASSERTION(childInstance, "Null actor!");
-
-    // unpack the arguments into a C format
-    int argc = aNames.Length();
-    NS_ASSERTION(argc == (int) aValues.Length(),
-                 "argn.length != argv.length");
-
-    nsAutoArrayPtr<char*> argn(new char*[1 + argc]);
-    nsAutoArrayPtr<char*> argv(new char*[1 + argc]);
-    argn[argc] = 0;
-    argv[argc] = 0;
-
-    for (int i = 0; i < argc; ++i) {
-        argn[i] = const_cast<char*>(NullableStringGet(aNames[i]));
-        argv[i] = const_cast<char*>(NullableStringGet(aValues[i]));
-    }
-
-    NPP npp = childInstance->GetNPP();
+    AssertPluginThread();
+    *rv = childInstance->DoNPP_New();
+    return true;
+}
 
-    // FIXME/cjones: use SAFE_CALL stuff
-    *rv = mFunctions.newp((char*)NullableStringGet(aMimeType),
-                          npp,
-                          aMode,
-                          argc,
-                          argn,
-                          argv,
-                          0);
-    if (NPERR_NO_ERROR != *rv) {
-        return true;
-    }
-
-    childInstance->Initialize();
-
-#if defined(XP_MACOSX) && defined(__i386__)
-    // If an i386 Mac OS X plugin has selected the Carbon event model then
-    // we have to fail. We do not support putting Carbon event model plugins
-    // out of process. Note that Carbon is the default model so out of process
-    // plugins need to actively negotiate something else in order to work
-    // out of process.
-    if (childInstance->EventModel() == NPEventModelCarbon) {
-      // Send notification that a plugin tried to negotiate Carbon NPAPI so that
-      // users can be notified that restarting the browser in i386 mode may allow
-      // them to use the plugin.
-      childInstance->SendNegotiatedCarbon();
-
-      // Fail to instantiate.
-      *rv = NPERR_MODULE_LOAD_FAILED_ERROR;
-    }
-#endif
-
+bool
+PluginModuleChild::RecvAsyncNPP_New(PPluginInstanceChild* aActor)
+{
+    PLUGIN_LOG_DEBUG_METHOD;
+    PluginInstanceChild* childInstance =
+        reinterpret_cast<PluginInstanceChild*>(aActor);
+    AssertPluginThread();
+    NPError rv = childInstance->DoNPP_New();
+    childInstance->SendAsyncNPP_NewResult(rv);
     return true;
 }
 
 bool
 PluginModuleChild::DeallocPPluginInstanceChild(PPluginInstanceChild* aActor)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -73,38 +73,42 @@ protected:
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
     virtual bool RecvSettingChanged(const PluginSettings& aSettings) MOZ_OVERRIDE;
 
     // Implement the PPluginModuleChild interface
     virtual bool RecvDisableFlashProtectedMode() MOZ_OVERRIDE;
     virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
     virtual bool AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv) MOZ_OVERRIDE;
+    virtual bool RecvAsyncNP_Initialize(const PluginSettings& aSettings) MOZ_OVERRIDE;
+    virtual bool AnswerSyncNPP_New(PPluginInstanceChild* aActor, NPError* rv)
+                                   MOZ_OVERRIDE;
+    virtual bool RecvAsyncNPP_New(PPluginInstanceChild* aActor) MOZ_OVERRIDE;
 
     virtual PPluginModuleChild*
     AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual PPluginInstanceChild*
     AllocPPluginInstanceChild(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
     DeallocPPluginInstanceChild(PPluginInstanceChild* aActor) MOZ_OVERRIDE;
 
     virtual bool
-    AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor,
-                                     const nsCString& aMimeType,
-                                     const uint16_t& aMode,
-                                     const InfallibleTArray<nsCString>& aNames,
-                                     const InfallibleTArray<nsCString>& aValues,
-                                     NPError* rv) MOZ_OVERRIDE;
+    RecvPPluginInstanceConstructor(PPluginInstanceChild* aActor,
+                                   const nsCString& aMimeType,
+                                   const uint16_t& aMode,
+                                   const InfallibleTArray<nsCString>& aNames,
+                                   const InfallibleTArray<nsCString>& aValues)
+                                   MOZ_OVERRIDE;
     virtual bool
     AnswerNP_Shutdown(NPError *rv) MOZ_OVERRIDE;
 
     virtual bool
     AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify,
                                      bool *aClearSiteData,
                                      bool *aGetSitesWithData) MOZ_OVERRIDE;
 
@@ -281,16 +285,17 @@ public:
         QUIRK_FLASH_AVOID_CGMODE_CRASHES                = 1 << 10,
     };
 
     int GetQuirks() { return mQuirks; }
 
     const PluginSettings& Settings() const { return mCachedSettings; }
 
 private:
+    NPError DoNP_Initialize(const PluginSettings& aSettings);
     void AddQuirk(PluginQuirks quirk) {
       if (mQuirks == QUIRKS_NOT_INITIALIZED)
         mQuirks = 0;
       mQuirks |= quirk;
     }
     void InitQuirksModes(const nsCString& aMimeType);
     bool InitGraphics();
     void DeinitGraphics();
--- 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
@@ -7,44 +7,48 @@
 #include "mozilla/plugins/PluginProcessParent.h"
 
 #include "base/string_util.h"
 #include "base/process_util.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 #include "mozilla/Telemetry.h"
+#include "nsThreadUtils.h"
 
 using std::vector;
 using std::string;
 
 using mozilla::ipc::BrowserProcessSubThread;
 using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::plugins::LaunchCompleteTask;
 using mozilla::plugins::PluginProcessParent;
 using base::ProcessArchitecture;
 
 template<>
 struct RunnableMethodTraits<PluginProcessParent>
 {
     static void RetainCallee(PluginProcessParent* obj) { }
     static void ReleaseCallee(PluginProcessParent* obj) { }
 };
 
 PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) :
     GeckoChildProcessHost(GeckoProcessType_Plugin),
-    mPluginFilePath(aPluginFilePath)
+    mPluginFilePath(aPluginFilePath),
+    mMainMsgLoop(MessageLoop::current()),
+    mRunCompleteTaskImmediately(false)
 {
 }
 
 PluginProcessParent::~PluginProcessParent()
 {
 }
 
 bool
-PluginProcessParent::Launch(int32_t timeoutMs)
+PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask)
 {
     ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
     uint32_t containerArchitectures = GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
 
     uint32_t pluginLibArchitectures = currentArchitecture;
 #ifdef XP_MACOSX
     nsresult rv = GetArchitecturesForBinary(mPluginFilePath.c_str(), &pluginLibArchitectures);
     if (NS_FAILED(rv)) {
@@ -69,28 +73,81 @@ PluginProcessParent::Launch(int32_t time
         else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) {
           selectedArchitecture = base::PROCESS_ARCH_ARM;
         }
         else {
             return false;
         }
     }
 
+    mLaunchCompleteTask = mozilla::Move(aLaunchCompleteTask);
+
     vector<string> args;
     args.push_back(MungePluginDsoPath(mPluginFilePath));
-    Telemetry::AutoTimer<Telemetry::PLUGIN_STARTUP_MS> timer;
-    return SyncLaunch(args, timeoutMs, selectedArchitecture);
+
+    bool result = AsyncLaunch(args, selectedArchitecture);
+    if (!result) {
+        mLaunchCompleteTask = nullptr;
+    }
+    return result;
 }
 
 void
 PluginProcessParent::Delete()
 {
   MessageLoop* currentLoop = MessageLoop::current();
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
 
   if (currentLoop == ioLoop) {
       delete this;
       return;
   }
 
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this, &PluginProcessParent::Delete));
 }
+
+void
+PluginProcessParent::SetCallRunnableImmediately(bool aCallImmediately)
+{
+    mRunCompleteTaskImmediately = aCallImmediately;
+}
+
+bool
+PluginProcessParent::WaitUntilConnected(int32_t aTimeoutMs)
+{
+    bool result = GeckoChildProcessHost::WaitUntilConnected(aTimeoutMs);
+    if (mRunCompleteTaskImmediately && mLaunchCompleteTask) {
+        if (result) {
+            mLaunchCompleteTask->SetLaunchSucceeded();
+        }
+        mLaunchCompleteTask->Run();
+        mLaunchCompleteTask = nullptr;
+    }
+    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();
+    if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
+        mMainMsgLoop->PostTask(FROM_HERE, mLaunchCompleteTask.release());
+    }
+}
+
+bool
+PluginProcessParent::IsConnected()
+{
+    mozilla::MonitorAutoLock lock(mMonitor);
+    return mProcessState == PROCESS_CONNECTED;
+}
+
--- a/dom/plugins/ipc/PluginProcessParent.h
+++ b/dom/plugins/ipc/PluginProcessParent.h
@@ -6,52 +6,83 @@
 
 #ifndef dom_plugins_PluginProcessParent_h
 #define dom_plugins_PluginProcessParent_h 1
 
 #include "mozilla/Attributes.h"
 #include "base/basictypes.h"
 
 #include "base/file_path.h"
+#include "base/task.h"
 #include "base/thread.h"
 #include "base/waitable_event.h"
 #include "chrome/common/child_process_host.h"
 
 #include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIRunnable.h"
 
 namespace mozilla {
 namespace plugins {
-//-----------------------------------------------------------------------------
+
+class LaunchCompleteTask : public Task
+{
+public:
+    LaunchCompleteTask()
+        : mLaunchSucceeded(false)
+    {
+    }
+
+    void SetLaunchSucceeded() { mLaunchSucceeded = true; }
+
+protected:
+    bool mLaunchSucceeded;
+};
 
 class PluginProcessParent : public mozilla::ipc::GeckoChildProcessHost
 {
 public:
     explicit PluginProcessParent(const std::string& aPluginFilePath);
     ~PluginProcessParent();
 
     /**
-     * Synchronously launch the plugin process. If the process fails to launch
-     * after timeoutMs, this method will return false.
+     * Launch the plugin process. If the process fails to launch,
+     * this method will return false.
+     *
+     * @param aLaunchCompleteTask Task that is executed on the main
+     * thread once the asynchonous launch has completed.
      */
-    bool Launch(int32_t timeoutMs);
+    bool Launch(UniquePtr<LaunchCompleteTask> aLaunchCompleteTask = UniquePtr<LaunchCompleteTask>());
 
     void Delete();
 
     virtual bool CanShutdown() MOZ_OVERRIDE
     {
         return true;
     }
 
     const std::string& GetPluginFilePath() { return mPluginFilePath; }
 
     using mozilla::ipc::GeckoChildProcessHost::GetShutDownEvent;
     using mozilla::ipc::GeckoChildProcessHost::GetChannel;
 
+    void SetCallRunnableImmediately(bool aCallImmediately);
+    virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0) MOZ_OVERRIDE;
+
+    virtual void OnChannelConnected(int32_t peer_pid) MOZ_OVERRIDE;
+    virtual void OnChannelError() MOZ_OVERRIDE;
+
+    bool IsConnected();
+
 private:
     std::string mPluginFilePath;
+    UniquePtr<LaunchCompleteTask> mLaunchCompleteTask;
+    MessageLoop* mMainMsgLoop;
+    bool mRunCompleteTaskImmediately;
 
     DISALLOW_EVIL_CONSTRUCTORS(PluginProcessParent);
 };
 
 
 } // namespace plugins
 } // namespace mozilla
 
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -1276,10 +1276,12 @@ PluginScriptableObjectChild::CollectForI
     }
     return PL_DHASH_NEXT;
 }
 
 /* static */ void
 PluginScriptableObjectChild::NotifyOfInstanceShutdown(PluginInstanceChild* aInstance)
 {
   AssertPluginThread();
-  sObjectMap->EnumerateEntries(CollectForInstance, aInstance);
+  if (sObjectMap) {
+    sObjectMap->EnumerateEntries(CollectForInstance, aInstance);
+  }
 }
--- a/dom/plugins/ipc/PluginScriptableObjectParent.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
@@ -7,16 +7,17 @@
 #include "PluginScriptableObjectParent.h"
 
 #include "jsapi.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/unused.h"
 #include "nsNPAPIPlugin.h"
+#include "PluginAsyncSurrogate.h"
 #include "PluginScriptableObjectUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 /**
  * NPIdentifiers in the chrome process are stored as jsids. The difficulty is in
@@ -105,16 +106,17 @@ FromNPIdentifier(NPIdentifier aIdentifie
 }
 
 namespace {
 
 inline void
 ReleaseVariant(NPVariant& aVariant,
                PluginInstanceParent* aInstance)
 {
+  PushSurrogateAcceptCalls acceptCalls(aInstance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
   if (npn) {
     npn->releasevariantvalue(&aVariant);
   }
 }
 
 } // anonymous namespace
 
@@ -638,16 +640,17 @@ PluginScriptableObjectParent::Initialize
 }
 
 NPObject*
 PluginScriptableObjectParent::CreateProxyObject()
 {
   NS_ASSERTION(mInstance, "Must have an instance!");
   NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
 
+  PushSurrogateAcceptCalls acceptCalls(mInstance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(mInstance);
 
   NPObject* npobject = npn->createobject(mInstance->GetNPP(),
                                          const_cast<NPClass*>(GetClass()));
   NS_ASSERTION(npobject, "Failed to create object?!");
   NS_ASSERTION(npobject->_class == GetClass(), "Wrong kind of object!");
   NS_ASSERTION(npobject->referenceCount == 1, "Some kind of live object!");
 
@@ -756,16 +759,17 @@ PluginScriptableObjectParent::AnswerHasM
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aHasMethod = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aHasMethod = false;
     return true;
   }
 
   StackIdentifier stackID(aId);
@@ -796,16 +800,17 @@ PluginScriptableObjectParent::AnswerInvo
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -885,16 +890,17 @@ PluginScriptableObjectParent::AnswerInvo
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -965,16 +971,17 @@ PluginScriptableObjectParent::AnswerHasP
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aHasProperty = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aHasProperty = false;
     return true;
   }
 
   StackIdentifier stackID(aId);
@@ -1007,16 +1014,17 @@ PluginScriptableObjectParent::AnswerGetP
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -1063,16 +1071,17 @@ PluginScriptableObjectParent::AnswerSetP
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aSuccess = false;
     return true;
   }
 
   NPVariant converted;
@@ -1109,16 +1118,17 @@ PluginScriptableObjectParent::AnswerRemo
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aSuccess = false;
     return true;
   }
 
   StackIdentifier stackID(aId);
@@ -1147,16 +1157,17 @@ PluginScriptableObjectParent::AnswerEnum
 
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_WARNING("No netscape funcs?!");
     *aSuccess = false;
     return true;
   }
 
   NPIdentifier* ids;
@@ -1199,16 +1210,17 @@ PluginScriptableObjectParent::AnswerCons
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
@@ -1291,16 +1303,17 @@ PluginScriptableObjectParent::AnswerNPN_
   PluginInstanceParent* instance = GetInstance();
   if (!instance) {
     NS_ERROR("No instance?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
+  PushSurrogateAcceptCalls acceptCalls(instance);
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
--- a/dom/plugins/ipc/PluginScriptableObjectParent.h
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.h
@@ -11,16 +11,17 @@
 #include "mozilla/plugins/PluginMessageUtils.h"
 
 #include "npfunctions.h"
 #include "npruntime.h"
 
 namespace mozilla {
 namespace plugins {
 
+class PluginAsyncSurrogate;
 class PluginInstanceParent;
 class PluginScriptableObjectParent;
 
 struct ParentNPObject : NPObject
 {
   ParentNPObject()
     : NPObject(), parent(nullptr), invalidated(false) { }
 
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -16,17 +16,19 @@ EXPORTS.mozilla.plugins += [
     'BrowserStreamChild.h',
     'BrowserStreamParent.h',
     'ChildAsyncCall.h',
     'ChildTimer.h',
     'NPEventAndroid.h',
     'NPEventOSX.h',
     'NPEventUnix.h',
     'NPEventWindows.h',
+    'PluginAsyncSurrogate.h',
     'PluginBridge.h',
+    'PluginDataResolver.h',
     'PluginInstanceChild.h',
     'PluginInstanceParent.h',
     'PluginMessageUtils.h',
     'PluginModuleChild.h',
     'PluginModuleParent.h',
     'PluginProcessChild.h',
     'PluginProcessParent.h',
     'PluginScriptableObjectChild.h',
@@ -75,16 +77,17 @@ if CONFIG['MOZ_ENABLE_QT']:
         'PluginHelperQt.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'BrowserStreamChild.cpp',
     'BrowserStreamParent.cpp',
     'ChildAsyncCall.cpp',
     'ChildTimer.cpp',
+    'PluginAsyncSurrogate.cpp',
     'PluginBackgroundDestroyer.cpp',
     'PluginInstanceParent.cpp',
     'PluginMessageUtils.cpp',
     'PluginModuleParent.cpp',
     'PluginProcessChild.cpp',
     'PluginProcessParent.cpp',
     'PluginScriptableObjectChild.cpp',
     'PluginScriptableObjectParent.cpp',
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -173,19 +173,19 @@ LogMixedContentMessage(MixedContentTypes
       messageLookupKey.AssignLiteral("BlockMixedDisplayContent");
     } else {
       messageLookupKey.AssignLiteral("BlockMixedActiveContent");
     }
   } else {
     severityFlag = nsIScriptError::warningFlag;
     messageCategory.AssignLiteral("Mixed Content Message");
     if (aClassification == eMixedDisplay) {
-      messageLookupKey.AssignLiteral("LoadingMixedDisplayContent");
+      messageLookupKey.AssignLiteral("LoadingMixedDisplayContent2");
     } else {
-      messageLookupKey.AssignLiteral("LoadingMixedActiveContent");
+      messageLookupKey.AssignLiteral("LoadingMixedActiveContent2");
     }
   }
 
   nsAutoCString locationSpec;
   aContentLocation->GetSpec(locationSpec);
   NS_ConvertUTF8toUTF16 locationSpecUTF16(locationSpec);
 
   const char16_t* strings[] = { locationSpecUTF16.get() };
--- a/editor/libeditor/IMETextTxn.cpp
+++ b/editor/libeditor/IMETextTxn.cpp
@@ -37,17 +37,19 @@ IMETextTxn::~IMETextTxn()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(IMETextTxn, EditTxn,
                                    mTextNode)
 // mRangeList can't lead to cycles
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMETextTxn)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsITransaction, IMETextTxn)
+  if (aIID.Equals(NS_GET_IID(IMETextTxn))) {
+    foundInterface = static_cast<nsITransaction*>(this);
+  } else
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMPL_ADDREF_INHERITED(IMETextTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(IMETextTxn, EditTxn)
 
 NS_IMETHODIMP
 IMETextTxn::DoTransaction()
 {
--- a/editor/libeditor/InsertTextTxn.cpp
+++ b/editor/libeditor/InsertTextTxn.cpp
@@ -33,17 +33,19 @@ InsertTextTxn::~InsertTextTxn()
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTxn, EditTxn,
                                    mTextNode)
 
 NS_IMPL_ADDREF_INHERITED(InsertTextTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(InsertTextTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertTextTxn)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsITransaction, InsertTextTxn)
+  if (aIID.Equals(NS_GET_IID(InsertTextTxn))) {
+    foundInterface = static_cast<nsITransaction*>(this);
+  } else
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 
 NS_IMETHODIMP
 InsertTextTxn::DoTransaction()
 {
   nsresult res = mTextNode->InsertData(mOffset, mStringToInsert);
   NS_ENSURE_SUCCESS(res, res);
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -149,8 +149,9 @@ skip-if = toolkit == 'android' # bug 105
 [test_dom_input_event_on_texteditor.html]
 [test_keypress_untrusted_event.html]
 [test_root_element_replacement.html]
 [test_select_all_without_body.html]
 skip-if = e10s
 [test_spellcheck_pref.html]
 skip-if = toolkit == 'android'
 [test_bug1068979.html]
+[test_bug1109465.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1109465.html
@@ -0,0 +1,69 @@
+<!DOCTYPE>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1109465
+-->
+<head>
+  <title>Test for Bug 1109465</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="display">
+  <textarea></textarea>
+</div>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 1109465 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  var t = document.querySelector("textarea");
+  t.focus();
+
+  // Type foo\nbar and place the caret at the end of the last line
+  synthesizeKey("f", {});
+  synthesizeKey("o", {});
+  synthesizeKey("o", {});
+  synthesizeKey("VK_RETURN", {});
+  synthesizeKey("b", {});
+  synthesizeKey("a", {});
+  synthesizeKey("r", {});
+  synthesizeKey("VK_UP", {});
+  is(t.selectionStart, 3, "Correct start of selection");
+  is(t.selectionEnd, 3, "Correct end of selection");
+
+  // Compose an IME string
+  synthesizeComposition({ type: "compositionstart" });
+  var composingString = "\u306B";
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": composingString,
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+  synthesizeComposition({ type: "compositioncommitasis" });
+  is(t.value, "foo\u306B\nbar", "Correct value after composition");
+
+  // Now undo to test that the transaction merger has correctly detected the
+  // IMETextTxn.
+  synthesizeKey("Z", {accelKey: true});
+  is(t.value, "foo\nbar", "Correct value after undo");
+
+  SimpleTest.finish();
+});
+
+</script>
+</body>
+
+</html>
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -310,27 +310,57 @@ void GeckoChildProcessHost::InitWindowsG
 }
 #endif
 
 bool
 GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
 {
   PrepareLaunch();
 
-  PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? 
-    PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI");
 
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this,
                                      &GeckoChildProcessHost::RunPerformAsyncLaunch,
                                      aExtraOpts, arch));
+
+  return WaitUntilConnected(aTimeoutMs);
+}
+
+bool
+GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts,
+                                   base::ProcessArchitecture arch)
+{
+  PrepareLaunch();
+
+  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+  ioLoop->PostTask(FROM_HERE,
+                   NewRunnableMethod(this,
+                                     &GeckoChildProcessHost::RunPerformAsyncLaunch,
+                                     aExtraOpts, arch));
+
+  // This may look like the sync launch wait, but we only delay as
+  // long as it takes to create the channel.
+  MonitorAutoLock lock(mMonitor);
+  while (mProcessState < CHANNEL_INITIALIZED) {
+    lock.Wait();
+  }
+
+  return true;
+}
+
+bool
+GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs)
+{
   // NB: this uses a different mechanism than the chromium parent
   // class.
+  PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? 
+    PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
+
   MonitorAutoLock lock(mMonitor);
   PRIntervalTime waitStart = PR_IntervalNow();
   PRIntervalTime current;
 
   // We'll receive several notifications, we need to exit when we
   // have either successfully launched or have timed out.
   while (mProcessState != PROCESS_CONNECTED) {
     // If there was an error then return it, don't wait out the timeout.
@@ -350,37 +380,16 @@ GeckoChildProcessHost::SyncLaunch(std::v
       waitStart = current;
     }
   }
 
   return mProcessState == PROCESS_CONNECTED;
 }
 
 bool
-GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
-{
-  PrepareLaunch();
-
-  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
-  ioLoop->PostTask(FROM_HERE,
-                   NewRunnableMethod(this,
-                                     &GeckoChildProcessHost::RunPerformAsyncLaunch,
-                                     aExtraOpts, base::GetCurrentProcessArchitecture()));
-
-  // This may look like the sync launch wait, but we only delay as
-  // long as it takes to create the channel.
-  MonitorAutoLock lock(mMonitor);
-  while (mProcessState < CHANNEL_INITIALIZED) {
-    lock.Wait();
-  }
-
-  return true;
-}
-
-bool
 GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
 {
   PrepareLaunch();
 
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this,
                                      &GeckoChildProcessHost::RunPerformAsyncLaunch,
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -47,17 +47,20 @@ public:
 
   static nsresult GetArchitecturesForBinary(const char *path, uint32_t *result);
 
   static uint32_t GetSupportedArchitecturesForProcessType(GeckoProcessType type);
 
   // Block until the IPC channel for our subprocess is initialized,
   // but no longer.  The child process may or may not have been
   // created when this method returns.
-  bool AsyncLaunch(StringVector aExtraOpts=StringVector());
+  bool AsyncLaunch(StringVector aExtraOpts=StringVector(),
+                   base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
+
+  virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0);
 
   // Block until the IPC channel for our subprocess is initialized and
   // the OS process is created.  The subprocess may or may not have
   // connected back to us when this method returns.
   //
   // NB: on POSIX, this method is relatively cheap, and doesn't
   // require disk IO.  On win32 however, it requires at least the
   // analogue of stat().  This difference induces a semantic
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -293,16 +293,17 @@ MessageChannel::MessageChannel(MessageLi
     mDispatchingSyncMessagePriority(0),
     mDispatchingAsyncMessage(false),
     mDispatchingAsyncMessagePriority(0),
     mCurrentTransaction(0),
     mTimedOutMessageSeqno(0),
     mRecvdErrors(0),
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
+    mIsWaitingForIncoming(false),
     mAbortOnError(false),
     mBlockScripts(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
     mPeerPid(-1)
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
@@ -659,17 +660,18 @@ MessageChannel::OnMessageReceivedFromLin
             mPending.erase((++it).base());
         } else {
             // No other messages with the same type/destination exist.
             compress = false;
         }
     }
 
     bool shouldWakeUp = AwaitingInterruptReply() ||
-                        (AwaitingSyncReply() && !ShouldDeferMessage(aMsg));
+                        (AwaitingSyncReply() && !ShouldDeferMessage(aMsg)) ||
+                        AwaitingIncomingMessage();
 
     // There are three cases we're concerned about, relating to the state of the
     // main thread:
     //
     // (1) We are waiting on a sync reply - main thread is blocked on the
     //     IPC monitor.
     //   - If the message is high priority, we wake up the main thread to
     //     deliver the message depending on ShouldDeferMessage. Otherwise, we
@@ -983,16 +985,45 @@ MessageChannel::Call(Message* aMsg, Mess
             return false;
         }
     }
 
     return true;
 }
 
 bool
+MessageChannel::WaitForIncomingMessage()
+{
+#ifdef OS_WIN
+    SyncStackFrame frame(this, true);
+#endif
+
+    { // Scope for lock
+        MonitorAutoLock lock(*mMonitor);
+        AutoEnterWaitForIncoming waitingForIncoming(*this);
+        if (mChannelState != ChannelConnected) {
+            return false;
+        }
+        if (!HasPendingEvents()) {
+            return WaitForInterruptNotify();
+        }
+    }
+
+    return OnMaybeDequeueOne();
+}
+
+bool
+MessageChannel::HasPendingEvents()
+{
+    AssertWorkerThread();
+    mMonitor->AssertCurrentThreadOwns();
+    return Connected() && !mPending.empty();
+}
+
+bool
 MessageChannel::InterruptEventOccurred()
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
     IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
 
     return (!Connected() ||
             !mPending.empty() ||
@@ -1541,17 +1572,17 @@ void
 MessageChannel::OnChannelErrorFromLink()
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
     if (InterruptStackDepth() > 0)
         NotifyWorkerThread();
 
-    if (AwaitingSyncReply())
+    if (AwaitingSyncReply() || AwaitingIncomingMessage())
         NotifyWorkerThread();
 
     if (ChannelClosing != mChannelState) {
         if (mAbortOnError) {
             NS_RUNTIMEABORT("Aborting on channel error.");
         }
         mChannelState = ChannelError;
         mMonitor->Notify();
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -119,16 +119,19 @@ class MessageChannel : HasResultCodes
     bool Echo(Message* aMsg);
 
     // Synchronously send |msg| (i.e., wait for |reply|)
     bool Send(Message* aMsg, Message* aReply);
 
     // Make an Interrupt call to the other side of the channel
     bool Call(Message* aMsg, Message* aReply);
 
+    // Wait until a message is received
+    bool WaitForIncomingMessage();
+
     bool CanSend() const;
 
     void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
     bool IsOnCxxStack() const {
         return !mCxxStackFrames.empty();
     }
 
@@ -209,16 +212,17 @@ class MessageChannel : HasResultCodes
     bool MaybeHandleError(Result code, const Message& aMsg, const char* channelName);
 
     void Clear();
 
     // Send OnChannelConnected notification to listeners.
     void DispatchOnChannelConnected();
 
     bool InterruptEventOccurred();
+    bool HasPendingEvents();
 
     bool ProcessPendingRequest(const Message &aUrgent);
 
     void MaybeUndeferIncall();
     void EnqueuePendingMessages();
 
     // Executed on the worker thread. Dequeues one pending message.
     bool OnMaybeDequeueOne();
@@ -314,16 +318,40 @@ class MessageChannel : HasResultCodes
     int AwaitingSyncReplyPriority() const {
         mMonitor->AssertCurrentThreadOwns();
         return mAwaitingSyncReplyPriority;
     }
     bool AwaitingInterruptReply() const {
         mMonitor->AssertCurrentThreadOwns();
         return !mInterruptStack.empty();
     }
+    bool AwaitingIncomingMessage() const {
+        mMonitor->AssertCurrentThreadOwns();
+        return mIsWaitingForIncoming;
+    }
+
+    class MOZ_STACK_CLASS AutoEnterWaitForIncoming
+    {
+    public:
+        explicit AutoEnterWaitForIncoming(MessageChannel& aChannel)
+            : mChannel(aChannel)
+        {
+            aChannel.mMonitor->AssertCurrentThreadOwns();
+            aChannel.mIsWaitingForIncoming = true;
+        }
+
+        ~AutoEnterWaitForIncoming()
+        {
+            mChannel.mIsWaitingForIncoming = false;
+        }
+
+    private:
+        MessageChannel& mChannel;
+    };
+    friend class AutoEnterWaitForIncoming;
 
     // Returns true if we're dispatching a sync message's callback.
     bool DispatchingSyncMessage() const {
         AssertWorkerThread();
         return mDispatchingSyncMessage;
     }
 
     int DispatchingSyncMessagePriority() const {
@@ -634,16 +662,21 @@ class MessageChannel : HasResultCodes
     // protected by mMonitor.  It is managed exclusively by the helper
     // |class CxxStackFrame|.
     mozilla::Vector<InterruptFrame> mCxxStackFrames;
 
     // Did we process an Interrupt out-call during this stack?  Only meaningful in
     // ExitedCxxStack(), from which this variable is reset.
     bool mSawInterruptOutMsg;
 
+    // Are we waiting on this channel for an incoming message? This is used
+    // to implement WaitForIncomingMessage(). Must only be accessed while owning
+    // mMonitor.
+    bool mIsWaitingForIncoming;
+
     // Map of replies received "out of turn", because of Interrupt
     // in-calls racing with replies to outstanding in-calls.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
     MessageMap mOutOfTurnReplies;
 
     // Stack of Interrupt in-calls that were deferred because of race
     // conditions.
     std::stack<Message> mDeferred;
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -961,17 +961,17 @@ MessageChannel::WaitForInterruptNotify()
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Re-use sync notification wait code if this channel does not require
   // Windows message deferral behavior. 
   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
     return WaitForSyncNotify();
   }
 
-  if (!InterruptStackDepth()) {
+  if (!InterruptStackDepth() && !AwaitingIncomingMessage()) {
     // There is currently no way to recover from this condition.
     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   }
 
   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
                "Shouldn't be here for channels that don't use message deferral!");
   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1668,27 +1668,27 @@ TimesAccessed(JSContext *cx, unsigned ar
     args.rval().setInt32(++accessed);
     return true;
 }
 
 static bool
 EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
     args.rval().setBoolean(TraceLoggerEnable(logger, cx));
 
     return true;
 }
 
 static bool
 DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
     args.rval().setBoolean(TraceLoggerDisable(logger));
 
     return true;
 }
 
 #ifdef DEBUG
 static bool
 DumpObject(JSContext *cx, unsigned argc, jsval *vp)
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -158,16 +158,63 @@ function TypedArrayIndexOf(searchElement
         if (O[k] === searchElement)
             return k;
     }
 
     // Step 13.
     return -1;
 }
 
+// ES6 draft rev30 (2014/12/24) 22.2.3.14 %TypedArray%.prototype.join(separator).
+function TypedArrayJoin(separator) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, separator, "TypedArrayJoin");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Steps 6-7.
+    var sep = separator === undefined ? "," : ToString(separator);
+
+    // Step 8.
+    if (len === 0)
+        return "";
+
+    // Step 9.
+    var element0 = O[0];
+
+    // Steps 10-11.
+    // Omit the 'if' clause in step 10, since typed arrays can not have undefined or null elements.
+    var R = ToString(element0);
+
+    // Steps 12-13.
+    for (var k = 1; k < len; k++) {
+        // Step 13.a.
+        var S = R + sep;
+
+        // Step 13.b.
+        var element = O[k];
+
+        // Steps 13.c-13.d.
+        // Omit the 'if' clause in step 13.c, since typed arrays can not have undefined or null elements.
+        var next = ToString(element);
+
+        // Step 13.e.
+        R = S + next;
+    }
+
+    // Step 14.
+    return R;
+}
+
 // ES6 draft rev29 (2014/12/06) 22.2.3.16 %TypedArray%.prototype.lastIndexOf(searchElement [,fromIndex]).
 function TypedArrayLastIndexOf(searchElement, fromIndex = undefined) {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
         return callFunction(CallTypedArrayMethodIfWrapped, this, searchElement, fromIndex,
                             "TypedArrayLastIndexOf");
     }
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2980,26 +2980,26 @@ fi # COMPILE_ENVIRONMENT
 AC_SUBST(MOZ_OPTIMIZE)
 AC_SUBST(MOZ_FRAMEPTR_FLAGS)
 AC_SUBST(MOZ_OPTIMIZE_FLAGS)
 AC_SUBST(MOZ_OPTIMIZE_LDFLAGS)
 AC_SUBST(MOZ_OPTIMIZE_SIZE_TWEAK)
 AC_SUBST(MOZ_PGO_OPTIMIZE_FLAGS)
 
 dnl ========================================================
-dnl = Enable trace logging
+dnl = Disable trace logging
 dnl ========================================================
-MOZ_ARG_ENABLE_BOOL(trace-logging,
-[  --enable-trace-logging   Enable trace logging],
-    ENABLE_TRACE_LOGGING=1,
+ENABLE_TRACE_LOGGING=1
+MOZ_ARG_DISABLE_BOOL(trace-logging,
+[  --disable-trace-logging   Disable trace logging],
     ENABLE_TRACE_LOGGING= )
 
 AC_SUBST(ENABLE_TRACE_LOGGING)
 
-if test "$ENABLE_TRACE_LOGGING"; then
+if test -n "$ENABLE_TRACE_LOGGING"; then
     AC_DEFINE(JS_TRACE_LOGGING)
 fi
 
 dnl ========================================================
 dnl = Disable treating compiler warnings as errors
 dnl ========================================================
 if test -z "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
    WARNINGS_AS_ERRORS=''
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -215,24 +215,24 @@ frontend::CompileScript(ExclusiveContext
                         JSString *source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionTask *extraSct /* = nullptr */)
 {
     MOZ_ASSERT(srcBuf.get());
 
     RootedString source(cx, source_);
 
-    js::TraceLogger *logger = nullptr;
+    js::TraceLoggerThread *logger = nullptr;
     if (cx->isJSContext())
         logger = TraceLoggerForMainThread(cx->asJSContext()->runtime());
     else
         logger = TraceLoggerForCurrentThread();
-    uint32_t logId = js::TraceLogCreateTextId(logger, options);
-    js::AutoTraceLog scriptLogger(logger, logId);
-    js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript);
+    js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
+    js::AutoTraceLog scriptLogger(logger, event);
+    js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileScript);
 
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
     MOZ_ASSERT_IF(evalCaller, options.compileAndGo);
     MOZ_ASSERT_IF(evalCaller, options.forEval);
     MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption);
@@ -468,20 +468,20 @@ frontend::CompileLazyFunction(JSContext 
     CompileOptions options(cx, lazy->version());
     options.setMutedErrors(lazy->mutedErrors())
            .setFileAndLine(lazy->source()->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setCompileAndGo(true)
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
-    js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
-    uint32_t logId = js::TraceLogCreateTextId(logger, options);
-    js::AutoTraceLog scriptLogger(logger, logId);
-    js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileLazy);
+    js::TraceLoggerThread *logger = js::TraceLoggerForMainThread(cx->runtime());
+    js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
+    js::AutoTraceLog scriptLogger(logger, event);
+    js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileLazy);
 
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, nullptr, lazy);
     if (!parser.checkOptions())
         return false;
 
     uint32_t staticLevel = lazy->staticLevel(cx);
 
@@ -526,20 +526,20 @@ frontend::CompileLazyFunction(JSContext 
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 static bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options,
                     const AutoNameVector &formals, SourceBufferHolder &srcBuf,
                     HandleObject enclosingScope, GeneratorKind generatorKind)
 {
-    js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
-    uint32_t logId = js::TraceLogCreateTextId(logger, options);
-    js::AutoTraceLog scriptLogger(logger, logId);
-    js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction);
+    js::TraceLoggerThread *logger = js::TraceLoggerForMainThread(cx->runtime());
+    js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
+    js::AutoTraceLog scriptLogger(logger, event);
+    js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileFunction);
 
     // FIXME: make Function pass in two strings and parse them as arguments and
     // ProgramElements respectively.
 
     if (!CheckLength(cx, srcBuf))
         return false;
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/tracelogger/drainTraceLogger.js
@@ -0,0 +1,88 @@
+function TestDrainTraceLoggerInvariants(obj) {
+    var scripts = 0;
+    var stops = 0;
+    for (var i = 0; i < objs.length; i++) {
+        if (objs[i].logType == "Scripts") {
+            scripts++;
+            assertEq("fileName" in objs[i], true); 
+            assertEq("lineNumber" in objs[i], true); 
+            assertEq("columnNumber" in objs[i], true); 
+        } else if (objs[i].logType == "Stop") {
+            stops++;
+        } else {
+            assertEq(true, false);
+        }
+    }
+    assertEq(scripts, stops);
+}
+
+function GetMaxScriptDepth(obj) {
+    var max_depth = 0;
+    for (var i = 0; i < objs.length; i++) {
+        if (objs[i].logType == "Stop")
+            depth--;
+        else {
+            depth++;
+            if (depth > max_depth)
+                max_depth = depth;
+        }
+    }
+    return max_depth;
+}
+
+function foo1() {
+    foo2();
+}
+function foo2() {
+
+}
+
+var du = new Debugger();
+if (typeof du.drainTraceLoggerTraces == "function") {
+print(1);
+    // Test normal setup.
+    du = new Debugger();
+    du.setupTraceLoggerForTraces();
+
+    du.startTraceLogger();
+    du.endTraceLogger();
+
+    var objs = du.drainTraceLoggerTraces();
+    TestDrainTraceLoggerInvariants(objs);
+    var empty_depth = GetMaxScriptDepth(objs);
+    var empty_length = objs.length;
+
+    // Test basic script.
+    for (var i=0; i<20; i++)
+        foo1();
+
+    du = new Debugger();
+    du.setupTraceLoggerTraces();
+
+    du.startTraceLogger();
+    foo1();
+    du.endTraceLogger();
+
+    var objs = du.drainTraceLoggerTraces();
+    TestDrainTraceLoggerInvariants(objs);
+    assertEq(empty_depth + 2 == GetMaxScriptDepth(objs));
+    assertEq(empty_length + 4 == GetMaxScriptDepth(objs));
+    
+    // Test basic script.
+    for (var i=0; i<20; i++)
+        foo1();
+
+    du = new Debugger();
+    du.setupTraceLoggerForTraces();
+
+    du.startTraceLogger();
+    for (var i=0; i<100; i++) {
+        foo1();
+    }
+    du.endTraceLogger();
+
+    var objs = du.drainTraceLoggerTraces();
+    TestDrainTraceLoggerInvariants(objs);
+    assertEq(empty_depth + 2 == GetMaxScriptDepth(objs));
+    assertEq(empty_length + 4*100 == GetMaxScriptDepth(objs));
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/tracelogger/setupTraceLogger.js
@@ -0,0 +1,70 @@
+
+var du = new Debugger();
+if (typeof du.setupTraceLogger == "function") {
+
+    // Try enabling.
+    assertEq(du.setupTraceLogger({
+        Scripts: true
+    }), true);
+
+    // No fail on re-enabling.
+    assertEq(du.setupTraceLogger({
+        Scripts: true
+    }), true);
+
+    // Try disabling.
+    assertEq(du.setupTraceLogger({
+        Scripts: false
+    }), true);
+
+    // No fail on re-disabling.
+    assertEq(du.setupTraceLogger({
+        Scripts: false
+    }), true);
+
+    // Throw exception if TraceLog item to report isn't found.
+    var success = du.setupTraceLogger({
+        Scripts: false,
+        Test: true
+    });
+    assertEq(success, false);
+
+    // SetupTraceLogger only enables individual items,
+    // when all items can be toggled.
+    du.startTraceLogger();
+    var obj = du.drainTraceLogger();
+    du.setupTraceLogger({
+        Scripts: true,
+        Test: true,
+    });
+    assertEq(du.drainTraceLogger().length, 0);
+    du.endTraceLogger();
+
+    // Expects an object as first argument.
+    succes = du.setupTraceLogger("blaat");
+    assertEq(succes, false);
+
+    // Expects an object as first argument.
+    succes = du.setupTraceLogger("blaat");
+    assertEq(succes, false);
+
+    // Expects an object as first argument.
+    failed = false;
+    try {
+        du.setupTraceLogger();
+    } catch (e) {
+        failed = true;
+    }
+    assertEq(failed, true);
+
+    // No problem with added to many arguments.
+    succes = du.setupTraceLogger({}, "test");
+    assertEq(succes, true);
+}
+
+var du2 = new Debugger();
+if (typeof du2.setupTraceLoggerForTraces == "function") {
+    du2.setupTraceLoggerForTraces({});
+    du2.setupTraceLoggerForTraces("test");
+    du2.setupTraceLoggerForTraces({}, "test");
+}
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -36,18 +36,18 @@ jit::Bailout(BailoutStack *sp, BaselineB
                "Fake jitTop pointer should be within the first page.");
     cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
 
     JitActivationIterator jitActivations(cx->runtime());
     BailoutFrameInfo bailoutData(jitActivations, sp);
     JitFrameIterator iter(jitActivations);
     MOZ_ASSERT(!iter.ionScript()->invalidated());
 
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    TraceLogTimestamp(logger, TraceLogger::Bailout);
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogTimestamp(logger, TraceLogger_Bailout);
 
     JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     MOZ_ASSERT(IsBaselineEnabled(cx));
 
     *bailoutInfo = nullptr;
     bool poppedLastSPSFrame = false;
     uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo,
@@ -102,18 +102,18 @@ jit::InvalidationBailout(InvalidationBai
 
     // We don't have an exit frame.
     cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
 
     JitActivationIterator jitActivations(cx->runtime());
     BailoutFrameInfo bailoutData(jitActivations, sp);
     JitFrameIterator iter(jitActivations);
 
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    TraceLogTimestamp(logger, TraceLogger::Invalidation);
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogTimestamp(logger, TraceLogger_Invalidation);
 
     JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     // Note: the frame size must be computed before we return from this function.
     *frameSizeOut = iter.frameSize();
 
     MOZ_ASSERT(IsBaselineEnabled(cx));
 
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1376,19 +1376,19 @@ jit::BailoutIonToBaseline(JSContext *cx,
                           const ExceptionBailoutInfo *excInfo, bool *poppedLastSPSFrameOut)
 {
     MOZ_ASSERT(bailoutInfo != nullptr);
     MOZ_ASSERT(*bailoutInfo == nullptr);
 
     MOZ_ASSERT(poppedLastSPSFrameOut);
     MOZ_ASSERT(!*poppedLastSPSFrameOut);
 
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    TraceLogStopEvent(logger, TraceLogger::IonMonkey);
-    TraceLogStartEvent(logger, TraceLogger::Baseline);
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogStopEvent(logger, TraceLogger_IonMonkey);
+    TraceLogStartEvent(logger, TraceLogger_Baseline);
 
     // The caller of the top frame must be one of the following:
     //      IonJS - Ion calling into Ion.
     //      BaselineStub - Baseline calling into Ion.
     //      Entry - Interpreter or other calling into Ion.
     //      Rectifier - Arguments rectifier calling into Ion.
     MOZ_ASSERT(iter.isBailoutJS());
     FrameType prevFrameType = iter.prevType();
@@ -1489,18 +1489,22 @@ jit::BailoutIonToBaseline(JSContext *cx,
 
     gc::AutoSuppressGC suppress(cx);
 
     while (true) {
         // Skip recover instructions as they are already recovered by |initInstructionResults|.
         snapIter.settleOnFrame();
 
         if (frameNo > 0) {
-            TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
-            TraceLogStartEvent(logger, TraceLogger::Baseline);
+            // TraceLogger doesn't create entries for inlined frames. But we
+            // see them in Baseline. Here we create the start events of those
+            // entries. So they correspond to what we will see in Baseline.
+            TraceLoggerEvent scriptEvent(logger, TraceLogger_Scripts, scr);
+            TraceLogStartEvent(logger, scriptEvent);
+            TraceLogStartEvent(logger, TraceLogger_Baseline);
         }
 
         JitSpew(JitSpew_BaselineBailouts, "    FrameNo %d", frameNo);
 
         // If we are bailing out to a catch or finally block in this frame,
         // pass excInfo to InitFromBailout and don't unpack any other frames.
         bool handleException = (catchingException && excInfo->frameNo() == frameNo);
 
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -76,19 +76,20 @@ MethodStatus
 BaselineCompiler::compile()
 {
     JitSpew(JitSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
             script->filename(), script->lineno(), script);
 
     JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%d",
             script->filename(), script->lineno());
 
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
-    AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation);
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerEvent scriptEvent(logger, TraceLogger_AnnotateScripts, script);
+    AutoTraceLog logScript(logger, scriptEvent);
+    AutoTraceLog logCompile(logger, TraceLogger_BaselineCompilation);
 
     if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
         return Method_Error;
 
     // Pin analysis info during compilation.
     types::AutoEnterAnalysis autoEnterAnalysis(cx);
 
     MOZ_ASSERT(!script->hasBaselineScript());
@@ -170,25 +171,31 @@ BaselineCompiler::compile()
     }
 
     if (pcEntries.oom())
         return Method_Error;
 
     prologueOffset_.fixup(&masm);
     epilogueOffset_.fixup(&masm);
     spsPushToggleOffset_.fixup(&masm);
+#ifdef JS_TRACE_LOGGING
+    traceLoggerEnterToggleOffset_.fixup(&masm);
+    traceLoggerExitToggleOffset_.fixup(&masm);
+#endif
     postDebugPrologueOffset_.fixup(&masm);
 
     // Note: There is an extra entry in the bytecode type map for the search hint, see below.
     size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
 
     mozilla::UniquePtr<BaselineScript, JS::DeletePolicy<BaselineScript> > baselineScript(
         BaselineScript::New(script, prologueOffset_.offset(),
                             epilogueOffset_.offset(),
                             spsPushToggleOffset_.offset(),
+                            traceLoggerEnterToggleOffset_.offset(),
+                            traceLoggerExitToggleOffset_.offset(),
                             postDebugPrologueOffset_.offset(),
                             icEntries_.length(),
                             pcMappingIndexEntries.length(),
                             pcEntries.length(),
                             bytecodeTypeMapEntries,
                             yieldOffsets_.length()));
     if (!baselineScript)
         return Method_Error;
@@ -234,16 +241,21 @@ BaselineCompiler::compile()
     // All barriers are emitted off-by-default, toggle them on if needed.
     if (cx->zone()->needsIncrementalBarrier())
         baselineScript->toggleBarriers(true);
 
     // All SPS instrumentation is emitted toggled off.  Toggle them on if needed.
     if (cx->runtime()->spsProfiler.enabled())
         baselineScript->toggleSPS(true);
 
+#ifdef JS_TRACE_LOGGING
+    // Initialize the tracelogger instrumentation.
+    baselineScript->initTraceLogger(cx->runtime(), script);
+#endif
+
     uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap();
     types::FillBytecodeTypeMap(script, bytecodeMap);
 
     // The last entry in the last index found, and is used to avoid binary
     // searches for the sought entry when queries are in linear order.
     bytecodeMap[script->nTypeSets()] = 0;
 
     baselineScript->copyYieldEntries(script, yieldOffsets_);
@@ -370,23 +382,18 @@ BaselineCompiler::emitPrologue()
         emitInitializeLocals(frame.nvars(), UndefinedValue());
     if (frame.nlexicals() > 0)
         emitInitializeLocals(frame.nlexicals(), MagicValue(JS_UNINITIALIZED_LEXICAL));
 
     if (needsEarlyStackCheck())
         masm.bind(&earlyStackCheckFailed);
 
 #ifdef JS_TRACE_LOGGING
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    Register loggerReg = RegisterSet::Volatile().takeGeneral();
-    masm.Push(loggerReg);
-    masm.movePtr(ImmPtr(logger), loggerReg);
-    masm.tracelogStart(loggerReg, TraceLogCreateTextId(logger, script));
-    masm.tracelogStart(loggerReg, TraceLogger::Baseline);
-    masm.Pop(loggerReg);
+    if (!emitTraceLoggerEnter())
+        return false;
 #endif
 
     // Record the offset of the prologue, because Ion can bailout before
     // the scope chain is initialized.
     prologueOffset_ = CodeOffsetLabel(masm.currentOffset());
 
     // Initialize the scope chain before any operation that may
     // call into the VM and trigger a GC.
@@ -420,25 +427,18 @@ BaselineCompiler::emitEpilogue()
 {
     // Record the offset of the epilogue, so we can do early return from
     // Debugger handlers during on-stack recompile.
     epilogueOffset_ = CodeOffsetLabel(masm.currentOffset());
 
     masm.bind(&return_);
 
 #ifdef JS_TRACE_LOGGING
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    Register loggerReg = RegisterSet::Volatile().takeGeneral();
-    masm.Push(loggerReg);
-    masm.movePtr(ImmPtr(logger), loggerReg);
-    masm.tracelogStop(loggerReg, TraceLogger::Baseline);
-    // Stop the script. Using a stop without checking the textId, since we
-    // we didn't save the textId for the script.
-    masm.tracelogStop(loggerReg);
-    masm.Pop(loggerReg);
+    if (!emitTraceLoggerExit())
+        return false;
 #endif
 
     // Pop SPS frame if necessary
     emitSPSPop();
 
     masm.mov(BaselineFrameReg, BaselineStackReg);
     masm.pop(BaselineFrameReg);
 
@@ -768,16 +768,74 @@ BaselineCompiler::emitDebugTrap()
     ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_DebugTrap);
     icEntry.setReturnOffset(CodeOffsetLabel(masm.currentOffset()));
     if (!icEntries_.append(icEntry))
         return false;
 
     return true;
 }
 
+#ifdef JS_TRACE_LOGGING
+bool
+BaselineCompiler::emitTraceLoggerEnter()
+{
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    RegisterSet regs = RegisterSet::Volatile();
+    Register loggerReg = regs.takeGeneral();
+    Register scriptReg = regs.takeGeneral();
+
+    Label noTraceLogger;
+    traceLoggerEnterToggleOffset_ = masm.toggledJump(&noTraceLogger);
+
+    masm.Push(loggerReg);
+    masm.Push(scriptReg);
+
+    masm.movePtr(ImmPtr(logger), loggerReg);
+
+    // Script start.
+    masm.movePtr(ImmGCPtr(script), scriptReg);
+    masm.loadPtr(Address(scriptReg, JSScript::offsetOfBaselineScript()), scriptReg);
+    Address scriptEvent(scriptReg, BaselineScript::offsetOfTraceLoggerScriptEvent());
+    masm.computeEffectiveAddress(scriptEvent, scriptReg);
+    masm.tracelogStartEvent(loggerReg, scriptReg);
+
+    // Engine start.
+    masm.tracelogStartId(loggerReg, TraceLogger_Baseline, /* force = */ true);
+
+    masm.Pop(scriptReg);
+    masm.Pop(loggerReg);
+
+    masm.bind(&noTraceLogger);
+
+    return true;
+}
+
+bool
+BaselineCompiler::emitTraceLoggerExit()
+{
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    Register loggerReg = RegisterSet::Volatile().takeGeneral();
+
+    Label noTraceLogger;
+    traceLoggerExitToggleOffset_ = masm.toggledJump(&noTraceLogger);
+
+    masm.Push(loggerReg);
+    masm.movePtr(ImmPtr(logger), loggerReg);
+
+    masm.tracelogStopId(loggerReg, TraceLogger_Baseline, /* force = */ true);
+    masm.tracelogStopId(loggerReg, TraceLogger_Scripts, /* force = */ true);
+
+    masm.Pop(loggerReg);
+
+    masm.bind(&noTraceLogger);
+
+    return true;
+}
+#endif
+
 bool
 BaselineCompiler::emitSPSPush()
 {
     // Enter the IC, guarded by a toggled jump (initially disabled).
     Label noPush;
     CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush);
     MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
     ICProfiler_Fallback::Compiler compiler(cx);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -249,16 +249,18 @@ class BaselineCompiler : public Baseline
     }
 
     bool emitStackCheck(bool earlyCheck=false);
     bool emitInterruptCheck();
     bool emitWarmUpCounterIncrement(bool allowOsr=true);
     bool emitArgumentTypeChecks();
     bool emitDebugPrologue();
     bool emitDebugTrap();
+    bool emitTraceLoggerEnter();
+    bool emitTraceLoggerExit();
     bool emitSPSPush();
     void emitSPSPop();
 
     bool initScopeChain();
 
     void storeValue(const StackValue *source, const Address &dest,
                     const ValueOperand &scratch);
 
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -37,27 +37,38 @@ PCMappingSlotInfo::ToSlotLocation(const 
         MOZ_ASSERT(stackVal->reg() == R1);
         return SlotInR1;
     }
     MOZ_ASSERT(stackVal->kind() != StackValue::Stack);
     return SlotIgnore;
 }
 
 BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
-                               uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset)
+                               uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                               uint32_t traceLoggerExitToggleOffset,
+                               uint32_t postDebugPrologueOffset)
   : method_(nullptr),
     templateScope_(nullptr),
     fallbackStubSpace_(),
     dependentAsmJSModules_(nullptr),
     prologueOffset_(prologueOffset),
     epilogueOffset_(epilogueOffset),
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
+#ifdef JS_TRACE_LOGGING
+# ifdef DEBUG
+    traceLoggerScriptsEnabled_(false),
+    traceLoggerEngineEnabled_(false),
+# endif
+    traceLoggerEnterToggleOffset_(traceLoggerEnterToggleOffset),
+    traceLoggerExitToggleOffset_(traceLoggerExitToggleOffset),
+    traceLoggerScriptEvent_(),
+#endif
     postDebugPrologueOffset_(postDebugPrologueOffset),
     flags_(0)
 { }
 
 static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
 
 static bool
 CheckFrame(InterpreterFrame *fp)
@@ -187,19 +198,19 @@ jit::EnterBaselineAtBranch(JSContext *cx
 
         // For eval function frames, set the callee token to the enclosing function.
         if (fp->isFunctionFrame())
             data.calleeToken = CalleeToToken(&fp->callee(), /* constructing = */ false);
         else
             data.calleeToken = CalleeToToken(fp->script());
     }
 
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    TraceLogStopEvent(logger, TraceLogger::Interpreter);
-    TraceLogStartEvent(logger, TraceLogger::Baseline);
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogStopEvent(logger, TraceLogger_Interpreter);
+    TraceLogStartEvent(logger, TraceLogger_Baseline);
 
     JitExecStatus status = EnterBaseline(cx, data);
     if (status != JitExec_Ok)
         return status;
 
     fp->setReturnValue(data.result);
     return JitExec_Ok;
 }
@@ -336,17 +347,18 @@ jit::CanEnterBaselineMethod(JSContext *c
     }
 
     RootedScript script(cx, state.script());
     return CanEnterBaselineJIT(cx, script, /* osrFrame = */ nullptr);
 };
 
 BaselineScript *
 BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset,
-                    uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset,
+                    uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                    uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset,
                     size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
                     size_t bytecodeTypeMapEntries, size_t yieldEntries)
 {
     static const unsigned DataAlignment = sizeof(uintptr_t);
 
     size_t icEntriesSize = icEntries * sizeof(ICEntry);
     size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
     size_t bytecodeTypeMapSize = bytecodeTypeMapEntries * sizeof(uint32_t);
@@ -363,17 +375,18 @@ BaselineScript::New(JSScript *jsscript, 
                         paddedPCMappingSize +
                         paddedBytecodeTypesMapSize +
                         paddedYieldEntriesSize;
 
     BaselineScript *script = jsscript->zone()->pod_malloc_with_extra<BaselineScript, uint8_t>(allocBytes);
     if (!script)
         return nullptr;
     new (script) BaselineScript(prologueOffset, epilogueOffset,
-                                spsPushToggleOffset, postDebugPrologueOffset);
+                                spsPushToggleOffset, traceLoggerEnterToggleOffset,
+                                traceLoggerExitToggleOffset, postDebugPrologueOffset);
 
     size_t offsetCursor = sizeof(BaselineScript);
     MOZ_ASSERT(offsetCursor == AlignBytes(sizeof(BaselineScript), DataAlignment));
 
     script->icEntriesOffset_ = offsetCursor;
     script->icEntries_ = icEntries;
     offsetCursor += paddedICEntriesSize;
 
@@ -890,16 +903,100 @@ BaselineScript::toggleSPS(bool enable)
         Assembler::ToggleToCmp(pushToggleLocation);
     else
         Assembler::ToggleToJmp(pushToggleLocation);
 #ifdef DEBUG
     spsOn_ = enable;
 #endif
 }
 
+#ifdef JS_TRACE_LOGGING
+void
+BaselineScript::initTraceLogger(JSRuntime *runtime, JSScript *script)
+{
+#ifdef DEBUG
+    traceLoggerScriptsEnabled_ = TraceLogTextIdEnabled(TraceLogger_Scripts);
+    traceLoggerEngineEnabled_ = TraceLogTextIdEnabled(TraceLogger_Engine);
+#endif
+
+    TraceLoggerThread *logger = TraceLoggerForMainThread(runtime);
+    if (TraceLogTextIdEnabled(TraceLogger_Scripts))
+        traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts, script);
+    else
+        traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts);
+
+    if (TraceLogTextIdEnabled(TraceLogger_Engine) || TraceLogTextIdEnabled(TraceLogger_Scripts)) {
+        CodeLocationLabel enter(method_, CodeOffsetLabel(traceLoggerEnterToggleOffset_));
+        CodeLocationLabel exit(method_, CodeOffsetLabel(traceLoggerExitToggleOffset_));
+        Assembler::ToggleToCmp(enter);
+        Assembler::ToggleToCmp(exit);
+    }
+}
+
+void
+BaselineScript::toggleTraceLoggerScripts(JSRuntime *runtime, JSScript *script, bool enable)
+{
+    bool engineEnabled = TraceLogTextIdEnabled(TraceLogger_Engine);
+
+    MOZ_ASSERT(enable == !traceLoggerScriptsEnabled_);
+    MOZ_ASSERT(engineEnabled == traceLoggerEngineEnabled_);
+
+    // Patch the logging script textId to be correct.
+    // When logging log the specific textId else the global Scripts textId.
+    TraceLoggerThread *logger = TraceLoggerForMainThread(runtime);
+    if (enable)
+        traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts, script);
+    else
+        traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts);
+
+    // Enable/Disable the traceLogger prologue and epilogue.
+    CodeLocationLabel enter(method_, CodeOffsetLabel(traceLoggerEnterToggleOffset_));
+    CodeLocationLabel exit(method_, CodeOffsetLabel(traceLoggerExitToggleOffset_));
+    if (!engineEnabled) {
+        if (enable) {
+            Assembler::ToggleToCmp(enter);
+            Assembler::ToggleToCmp(exit);
+        } else {
+            Assembler::ToggleToJmp(enter);
+            Assembler::ToggleToJmp(exit);
+        }
+    }
+
+#if DEBUG
+    traceLoggerScriptsEnabled_ = enable;
+#endif
+}
+
+void
+BaselineScript::toggleTraceLoggerEngine(bool enable)
+{
+    bool scriptsEnabled = TraceLogTextIdEnabled(TraceLogger_Scripts);
+
+    MOZ_ASSERT(enable == !traceLoggerEngineEnabled_);
+    MOZ_ASSERT(scriptsEnabled == traceLoggerScriptsEnabled_);
+
+    // Enable/Disable the traceLogger prologue and epilogue.
+    CodeLocationLabel enter(method_, CodeOffsetLabel(traceLoggerEnterToggleOffset_));
+    CodeLocationLabel exit(method_, CodeOffsetLabel(traceLoggerExitToggleOffset_));
+    if (!scriptsEnabled) {
+        if (enable) {
+            Assembler::ToggleToCmp(enter);
+            Assembler::ToggleToCmp(exit);
+        } else {
+            Assembler::ToggleToJmp(enter);
+            Assembler::ToggleToJmp(exit);
+        }
+    }
+
+#if DEBUG
+    traceLoggerEngineEnabled_ = enable;
+#endif
+}
+#endif
+
 void
 BaselineScript::purgeOptimizedStubs(Zone *zone)
 {
     JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
 
     for (size_t i = 0; i < numICEntries(); i++) {
         ICEntry &entry = icEntry(i);
         if (!entry.hasStub())
@@ -997,16 +1094,44 @@ jit::ToggleBaselineSPS(JSRuntime *runtim
             JSScript *script = i.get<JSScript>();
             if (!script->hasBaselineScript())
                 continue;
             script->baselineScript()->toggleSPS(enable);
         }
     }
 }
 
+#ifdef JS_TRACE_LOGGING
+void
+jit::ToggleBaselineTraceLoggerScripts(JSRuntime *runtime, bool enable)
+{
+    for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
+        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (!script->hasBaselineScript())
+                continue;
+            script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable);
+        }
+    }
+}
+
+void
+jit::ToggleBaselineTraceLoggerEngine(JSRuntime *runtime, bool enable)
+{
+    for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
+        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (!script->hasBaselineScript())
+                continue;
+            script->baselineScript()->toggleTraceLoggerEngine(enable);
+        }
+    }
+}
+#endif
+
 static void
 MarkActiveBaselineScripts(JSRuntime *rt, const JitActivationIterator &activation)
 {
     for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case JitFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -11,16 +11,17 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "ds/LifoAlloc.h"
 #include "jit/Bailouts.h"
 #include "jit/IonCode.h"
 #include "jit/MacroAssembler.h"
+#include "vm/TraceLogging.h"
 
 namespace js {
 namespace jit {
 
 class StackValue;
 class ICEntry;
 class ICStub;
 
@@ -139,16 +140,27 @@ struct BaselineScript
     uint32_t epilogueOffset_;
 
     // The offsets for the toggledJump instructions for SPS update ICs.
 #ifdef DEBUG
     mozilla::DebugOnly<bool> spsOn_;
 #endif
     uint32_t spsPushToggleOffset_;
 
+    // The offsets and event used for Tracelogger toggling.
+#ifdef JS_TRACE_LOGGING
+# ifdef DEBUG
+    bool traceLoggerScriptsEnabled_;
+    bool traceLoggerEngineEnabled_;
+# endif
+    uint32_t traceLoggerEnterToggleOffset_;
+    uint32_t traceLoggerExitToggleOffset_;
+    TraceLoggerEvent traceLoggerScriptEvent_;
+#endif
+
     // Native code offsets right after the debug prologue VM call returns, or
     // would have returned. This offset is recorded even when debug mode is
     // off to aid on-stack debug mode recompilation.
     //
     // We don't need one for the debug epilogue because that always happens
     // right before the epilogue, so we just use the epilogue offset.
     uint32_t postDebugPrologueOffset_;
 
@@ -197,21 +209,23 @@ struct BaselineScript
 
     // For generator scripts, we store the native code address for each yield
     // instruction.
     uint32_t yieldEntriesOffset_;
 
   public:
     // Do not call directly, use BaselineScript::New. This is public for cx->new_.
     BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
-                   uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset);
+                   uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                   uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset);
 
     static BaselineScript *New(JSScript *jsscript, uint32_t prologueOffset,
                                uint32_t epilogueOffset, uint32_t postDebugPrologueOffset,
-                               uint32_t spsPushToggleOffset, size_t icEntries,
+                               uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                               uint32_t traceLoggerExitToggleOffset, size_t icEntries,
                                size_t pcMappingIndexEntries, size_t pcMappingSize,
                                size_t bytecodeTypeMapEntries, size_t yieldEntries);
 
     static void Trace(JSTracer *trc, BaselineScript *script);
     static void Destroy(FreeOp *fop, BaselineScript *script);
 
     void purgeOptimizedStubs(Zone *zone);
 
@@ -381,16 +395,26 @@ struct BaselineScript
   public:
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is nullptr, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
 
     void toggleSPS(bool enable);
 
+#ifdef JS_TRACE_LOGGING
+    void initTraceLogger(JSRuntime *runtime, JSScript *script);
+    void toggleTraceLoggerScripts(JSRuntime *runtime, JSScript *script, bool enable);
+    void toggleTraceLoggerEngine(bool enable);
+
+    static size_t offsetOfTraceLoggerScriptEvent() {
+        return offsetof(BaselineScript, traceLoggerScriptEvent_);
+    }
+#endif
+
     void noteAccessedGetter(uint32_t pcOffset);
     void noteArrayWriteHole(uint32_t pcOffset);
 
     static size_t offsetOfFlags() {
         return offsetof(BaselineScript, flags_);
     }
     static size_t offsetOfYieldEntriesOffset() {
         return offsetof(BaselineScript, yieldEntriesOffset_);
@@ -433,16 +457,21 @@ FinishDiscardBaselineScript(FreeOp *fop,
 
 void
 AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, size_t *data,
                       size_t *fallbackStubs);
 
 void
 ToggleBaselineSPS(JSRuntime *runtime, bool enable);
 
+void
+ToggleBaselineTraceLoggerScripts(JSRuntime *runtime, bool enable);
+void
+ToggleBaselineTraceLoggerEngine(JSRuntime *runtime, bool enable);
+
 struct BaselineBailoutInfo
 {
     // Pointer into the current C stack, where overwriting will start.
     uint8_t *incomingStack;
 
     // The top and bottom heapspace addresses of the reconstructed stack
     // which will be copied to the bottom.
     uint8_t *copyStackTop;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2017,18 +2017,18 @@ void
 CodeGenerator::visitOsrEntry(LOsrEntry *lir)
 {
     // Remember the OSR entry offset into the code buffer.
     masm.flushBuffer();
     setOsrEntryOffset(masm.size());
 
 #ifdef JS_TRACE_LOGGING
     if (gen->info().executionMode() == SequentialExecution) {
-        emitTracelogStopEvent(TraceLogger::Baseline);
-        emitTracelogStartEvent(TraceLogger::IonMonkey);
+        emitTracelogStopEvent(TraceLogger_Baseline);
+        emitTracelogStartEvent(TraceLogger_IonMonkey);
     }
 #endif
 
     // Allocate the full frame for this function
     // Note we have a new entry here. So we reset MacroAssembler::framePushed()
     // to 0, before reserving the stack.
     MOZ_ASSERT(masm.framePushed() == frameSize());
     masm.setFramePushed(0);
@@ -7344,20 +7344,18 @@ CodeGenerator::generate()
     masm.setFramePushed(0);
     if (!generatePrologue())
         return false;
 
     masm.bind(&skipPrologue);
 
 #ifdef JS_TRACE_LOGGING
     if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
-        if (!emitTracelogScriptStart())
-            return false;
-        if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
-            return false;
+        emitTracelogScriptStart();
+        emitTracelogStartEvent(TraceLogger_IonMonkey);
     }
 #endif
 
 #ifdef DEBUG
     // Assert that the argument types are correct.
     generateArgumentsChecks(/* bailout = */ false);
 #endif
 
@@ -7620,29 +7618,35 @@ CodeGenerator::link(JSContext *cx, types
     if (graph.numConstants())
         ionScript->copyConstants(graph.constantPool());
     if (callTargets.length() > 0)
         ionScript->copyCallTargetEntries(callTargets.begin());
     if (patchableBackedges_.length() > 0)
         ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin(), masm);
 
 #ifdef JS_TRACE_LOGGING
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
     for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
         patchableTraceLoggers_[i].fixup(&masm);
         Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
                                            ImmPtr(logger),
                                            ImmPtr(nullptr));
     }
-    uint32_t scriptId = TraceLogCreateTextId(logger, script);
-    for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
-        patchableTLScripts_[i].fixup(&masm);
-        Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
-                                           ImmPtr((void *) uintptr_t(scriptId)),
-                                           ImmPtr((void *)0));
+
+    if (patchableTLScripts_.length() > 0) {
+        MOZ_ASSERT(TraceLogTextIdEnabled(TraceLogger_Scripts));
+        TraceLoggerEvent event(logger, TraceLogger_Scripts, script);
+        ionScript->setTraceLoggerEvent(event);
+        uint32_t textId = event.payload()->textId();
+        for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
+            patchableTLScripts_[i].fixup(&masm);
+            Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
+                                               ImmPtr((void *) uintptr_t(textId)),
+                                               ImmPtr((void *)0));
+        }
     }
 #endif
 
     switch (executionMode) {
       case SequentialExecution:
         // The correct state for prebarriers is unknown until the end of compilation,
         // since a GC can occur during code generation. All barriers are emitted
         // off-by-default, and are toggled on here if necessary.
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -543,19 +543,20 @@ jit::LazyLinkTopActivation(JSContext *cx
 
     types::AutoEnterAnalysis enterTypes(cx);
     RootedScript script(cx, builder->script());
 
     // Remove from pending.
     builder->remove();
 
     if (CodeGenerator *codegen = builder->backgroundCodegen()) {
-        js::TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-        AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
-        AutoTraceLog logLink(logger, TraceLogger::IonLinking);
+        js::TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+        TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
+        AutoTraceLog logScript(logger, event);
+        AutoTraceLog logLink(logger, TraceLogger_IonLinking);
 
         JitContext jctx(cx, &builder->alloc());
 
         // Root the assembler until the builder is finished below. As it
         // was constructed off thread, the assembler has not been rooted
         // previously, though any GC activity would discard the builder.
         codegen->masm.constructRoot(cx);
 
@@ -1267,17 +1268,17 @@ jit::ToggleBarriers(JS::Zone *zone, bool
 
 namespace js {
 namespace jit {
 
 bool
 OptimizeMIR(MIRGenerator *mir)
 {
     MIRGraph &graph = mir->graph();
-    TraceLogger *logger;
+    TraceLoggerThread *logger;
     if (GetJitContext()->runtime->onMainThread())
         logger = TraceLoggerForMainThread(GetJitContext()->runtime);
     else
         logger = TraceLoggerForCurrentThread();
 
     if (!mir->compilingAsmJS()) {
         if (!MakeMRegExpHoistable(graph))
             return false;
@@ -1285,59 +1286,59 @@ OptimizeMIR(MIRGenerator *mir)
 
     IonSpewPass("BuildSSA");
     AssertBasicGraphCoherency(graph);
 
     if (mir->shouldCancel("Start"))
         return false;
 
     if (!mir->compilingAsmJS()) {
-        AutoTraceLog log(logger, TraceLogger::FoldTests);
+        AutoTraceLog log(logger, TraceLogger_FoldTests);
         FoldTests(graph);
         IonSpewPass("Fold Tests");
         AssertBasicGraphCoherency(graph);
 
         if (mir->shouldCancel("Fold Tests"))
             return false;
     }
 
     {
-        AutoTraceLog log(logger, TraceLogger::SplitCriticalEdges);
+        AutoTraceLog log(logger, TraceLogger_SplitCriticalEdges);
         if (!SplitCriticalEdges(graph))
             return false;
         IonSpewPass("Split Critical Edges");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Split Critical Edges"))
             return false;
     }
 
     {
-        AutoTraceLog log(logger, TraceLogger::RenumberBlocks);
+        AutoTraceLog log(logger, TraceLogger_RenumberBlocks);
         if (!RenumberBlocks(graph))
             return false;
         IonSpewPass("Renumber Blocks");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Renumber Blocks"))
             return false;
     }
 
     {
-        AutoTraceLog log(logger, TraceLogger::DominatorTree);
+        AutoTraceLog log(logger, TraceLogger_DominatorTree);
         if (!BuildDominatorTree(graph))
             return false;
         // No spew: graph not changed.
 
         if (mir->shouldCancel("Dominator Tree"))
             return false;
     }
 
     {
-        AutoTraceLog log(logger, TraceLogger::PhiAnalysis);
+        AutoTraceLog log(logger, TraceLogger_PhiAnalysis);
         // Aggressive phi elimination must occur before any code elimination. If the
         // script contains a try-statement, we only compiled the try block and not
         // the catch or finally blocks, so in this case it's also invalid to use
         // aggressive phi elimination.
         Observability observability = graph.hasTryBlock()
                                       ? ConservativeObservability
                                       : AggressiveObservability;
         if (!EliminatePhis(mir, graph, observability))
@@ -1353,43 +1354,43 @@ OptimizeMIR(MIRGenerator *mir)
         AssertExtendedGraphCoherency(graph);
         // No spew: graph not changed.
 
         if (mir->shouldCancel("Phi reverse mapping"))
             return false;
     }
 
     if (mir->optimizationInfo().scalarReplacementEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::ScalarReplacement);
+        AutoTraceLog log(logger, TraceLogger_ScalarReplacement);
         if (!ScalarReplacement(mir, graph))
             return false;
         IonSpewPass("Scalar Replacement");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Scalar Replacement"))
             return false;
     }
 
     if (!mir->compilingAsmJS()) {
-        AutoTraceLog log(logger, TraceLogger::ApplyTypes);
+        AutoTraceLog log(logger, TraceLogger_ApplyTypes);
         if (!ApplyTypeInformation(mir, graph))
             return false;
         IonSpewPass("Apply types");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Apply types"))
             return false;
     }
 
     // Parallel Safety Analysis. Note that this may delete blocks containing
     // instructions pointed to by the dependency() field of instructions which
     // are not deleted, leaving them dangling. This is ok, since we'll rerun
     // AliasAnalysis, which recomputes them, before they're needed.
     if (graph.entryBlock()->info().executionMode() == ParallelExecution) {
-        AutoTraceLog log(logger, TraceLogger::ParallelSafetyAnalysis);
+        AutoTraceLog log(logger, TraceLogger_ParallelSafetyAnalysis);
         ParallelSafetyAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return false;
         IonSpewPass("Parallel Safety Analysis");
         AssertExtendedGraphCoherency(graph);
         if (mir->shouldCancel("Parallel Safety Analysis"))
             return false;
     }
@@ -1398,17 +1399,17 @@ OptimizeMIR(MIRGenerator *mir)
     if (!gvn.init())
         return false;
 
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
     if (mir->optimizationInfo().licmEnabled() ||
         mir->optimizationInfo().gvnEnabled())
     {
-        AutoTraceLog log(logger, TraceLogger::AliasAnalysis);
+        AutoTraceLog log(logger, TraceLogger_AliasAnalysis);
         AliasAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return false;
         IonSpewPass("Alias analysis");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Alias analysis"))
             return false;
@@ -1421,45 +1422,45 @@ OptimizeMIR(MIRGenerator *mir)
                 return false;
 
             if (mir->shouldCancel("Eliminate dead resume point operands"))
                 return false;
         }
     }
 
     if (mir->optimizationInfo().gvnEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::GVN);
+        AutoTraceLog log(logger, TraceLogger_GVN);
         if (!gvn.run(ValueNumberer::UpdateAliasAnalysis))
             return false;
         IonSpewPass("GVN");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("GVN"))
             return false;
     }
 
     if (mir->optimizationInfo().licmEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::LICM);
+        AutoTraceLog log(logger, TraceLogger_LICM);
         // LICM can hoist instructions from conditional branches and trigger
         // repeated bailouts. Disable it if this script is known to bailout
         // frequently.
         JSScript *script = mir->info().script();
         if (!script || !script->hadFrequentBailouts()) {
             if (!LICM(mir, graph))
                 return false;
             IonSpewPass("LICM");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
         }
     }
 
     if (mir->optimizationInfo().rangeAnalysisEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::RangeAnalysis);
+        AutoTraceLog log(logger, TraceLogger_RangeAnalysis);
         RangeAnalysis r(mir, graph);
         if (!r.addBetaNodes())
             return false;
         IonSpewPass("Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA Beta"))
             return false;
@@ -1507,90 +1508,90 @@ OptimizeMIR(MIRGenerator *mir)
             IonSpewPass("Truncate Doubles");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("Truncate Doubles"))
                 return false;
         }
 
         if (mir->optimizationInfo().loopUnrollingEnabled()) {
-            AutoTraceLog log(logger, TraceLogger::LoopUnrolling);
+            AutoTraceLog log(logger, TraceLogger_LoopUnrolling);
 
             if (!UnrollLoops(graph, r.loopIterationBounds))
                 return false;
 
             IonSpewPass("Unroll Loops");
             AssertExtendedGraphCoherency(graph);
         }
     }
 
     if (mir->optimizationInfo().eaaEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::EffectiveAddressAnalysis);
+        AutoTraceLog log(logger, TraceLogger_EffectiveAddressAnalysis);
         EffectiveAddressAnalysis eaa(graph);
         if (!eaa.analyze())
             return false;
         IonSpewPass("Effective Address Analysis");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Effective Address Analysis"))
             return false;
     }
 
     {
-        AutoTraceLog log(logger, TraceLogger::EliminateDeadCode);
+        AutoTraceLog log(logger, TraceLogger_EliminateDeadCode);
         if (!EliminateDeadCode(mir, graph))
             return false;
         IonSpewPass("DCE");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("DCE"))
             return false;
     }
 
     {
-        AutoTraceLog log(logger, TraceLogger::EliminateDeadCode);
+        AutoTraceLog log(logger, TraceLogger_EliminateDeadCode);
         if (!Sink(mir, graph))
             return false;
         IonSpewPass("Sink");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Sink"))
             return false;
     }
 
     // Make loops contiguous. We do this after GVN/UCE and range analysis,
     // which can remove CFG edges, exposing more blocks that can be moved.
     {
-        AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
+        AutoTraceLog log(logger, TraceLogger_MakeLoopsContiguous);
         if (!MakeLoopsContiguous(graph))
             return false;
         IonSpewPass("Make loops contiguous");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Make loops contiguous"))
             return false;
     }
 
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
     if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::EdgeCaseAnalysis);
+        AutoTraceLog log(logger, TraceLogger_EdgeCaseAnalysis);
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
             return false;
         IonSpewPass("Edge Case Analysis (Late)");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Edge Case Analysis (Late)"))
             return false;
     }
 
     if (mir->optimizationInfo().eliminateRedundantChecksEnabled()) {
-        AutoTraceLog log(logger, TraceLogger::EliminateRedundantChecks);
+        AutoTraceLog log(logger, TraceLogger_EliminateRedundantChecks);
         // Note: check elimination has to run after all other passes that move
         // instructions. Since check uses are replaced with the actual index,
         // code motion after this pass could incorrectly move a load or store
         // before its bounds check.
         if (!EliminateRedundantChecks(graph))
             return false;
         IonSpewPass("Bounds Check Elimination");
         AssertGraphCoherency(graph);
@@ -1599,41 +1600,41 @@ OptimizeMIR(MIRGenerator *mir)
     return true;
 }
 
 LIRGraph *
 GenerateLIR(MIRGenerator *mir)
 {
     MIRGraph &graph = mir->graph();
 
-    TraceLogger *logger;
+    TraceLoggerThread *logger;
     if (GetJitContext()->runtime->onMainThread())
         logger = TraceLoggerForMainThread(GetJitContext()->runtime);
     else
         logger = TraceLoggerForCurrentThread();
 
     LIRGraph *lir = mir->alloc().lifoAlloc()->new_<LIRGraph>(&graph);
     if (!lir || !lir->init())
         return nullptr;
 
     LIRGenerator lirgen(mir, graph, *lir);
     {
-        AutoTraceLog log(logger, TraceLogger::GenerateLIR);
+        AutoTraceLog log(logger, TraceLogger_GenerateLIR);
         if (!lirgen.generate())
             return nullptr;
         IonSpewPass("Generate LIR");
 
         if (mir->shouldCancel("Generate LIR"))
             return nullptr;
     }
 
     AllocationIntegrityState integrity(*lir);
 
     {
-        AutoTraceLog log(logger, TraceLogger::RegisterAllocation);
+        AutoTraceLog log(logger, TraceLogger_RegisterAllocation);
 
         switch (mir->optimizationInfo().registerAllocator()) {
           case RegisterAllocator_LSRA: {
 #ifdef DEBUG
             if (!integrity.record())
                 return nullptr;
 #endif
 
@@ -1693,22 +1694,22 @@ GenerateLIR(MIRGenerator *mir)
     }
 
     return lir;
 }
 
 CodeGenerator *
 GenerateCode(MIRGenerator *mir, LIRGraph *lir)
 {
-    TraceLogger *logger;
+    TraceLoggerThread *logger;
     if (GetJitContext()->runtime->onMainThread())
         logger = TraceLoggerForMainThread(GetJitContext()->runtime);
     else
         logger = TraceLoggerForCurrentThread();
-    AutoTraceLog log(logger, TraceLogger::GenerateCode);
+    AutoTraceLog log(logger, TraceLogger_GenerateCode);
 
     CodeGenerator *codegen = js_new<CodeGenerator>(mir, lir);
     if (!codegen)
         return nullptr;
 
     if (!codegen->generate()) {
         js_delete(codegen);
         return nullptr;
@@ -1740,17 +1741,17 @@ AttachFinishedCompilations(JSContext *cx
     if (!ion)
         return;
 
     types::AutoEnterAnalysis enterTypes(cx);
     AutoLockHelperThreadState lock;
 
     GlobalHelperThreadState::IonBuilderVector &finished = HelperThreadState().ionFinishedList();
 
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
 
     // Incorporate any off thread compilations for the compartment which have
     // finished, failed or have been cancelled.
     while (true) {
         IonBuilder *builder = nullptr;
 
         // Find a finished builder for the compartment.
         for (size_t i = 0; i < finished.length(); i++) {
@@ -1792,18 +1793,19 @@ AttachFinishedCompilations(JSContext *cx
                 HelperThreadState().ionLazyLinkList().insertFront(builder);
                 continue;
             }
         }
 
         if (CodeGenerator *codegen = builder->backgroundCodegen()) {
             RootedScript script(cx, builder->script());
             JitContext jctx(cx, &builder->alloc());
-            AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
-            AutoTraceLog logLink(logger, TraceLogger::IonLinking);
+            TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
+            AutoTraceLog logScript(logger, event);
+            AutoTraceLog logLink(logger, TraceLogger_IonLinking);
 
             // Root the assembler until the builder is finished below. As it
             // was constructed off thread, the assembler has not been rooted
             // previously, though any GC activity would discard the builder.
             codegen->masm.constructRoot(cx);
 
             bool success;
             {
@@ -1871,19 +1873,20 @@ TrackPropertiesForSingletonScopes(JSCont
 }
 
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
            BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
            ExecutionMode executionMode, bool recompile,
            OptimizationLevel optimizationLevel)
 {
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
-    AutoTraceLog logCompile(logger, TraceLogger::IonCompilation);
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
+    AutoTraceLog logScript(logger, event);
+    AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
 
     MOZ_ASSERT(optimizationLevel > Optimization_DontCompile);
 
     // Make sure the script's canonical function isn't lazy. We can't de-lazify
     // it in a helper thread.
     script->ensureNonLazyCanonicalFunction(cx);
 
     TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -16,16 +16,17 @@
 #include "jit/Ion.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/JitSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Opcodes.h"
 #include "vm/RegExpStatics.h"
+#include "vm/TraceLogging.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/CompileInfo-inl.h"
 #include "jit/ExecutionMode-inl.h"
@@ -337,16 +338,21 @@ IonBuilder::DontInline(JSScript *targetS
 }
 
 IonBuilder::InliningDecision
 IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
 {
     if (!optimizationInfo().inlineInterpreted())
         return InliningDecision_DontInline;
 
+    if (TraceLogTextIdEnabled(TraceLogger_InlinedScripts)) {
+        return DontInline(nullptr, "Tracelogging of inlined scripts is enabled"
+                                   "but Tracelogger cannot do that yet.");
+    }
+
     if (!target->isInterpreted())
         return DontInline(nullptr, "Non-interpreted target");
 
     // Allow constructing lazy scripts when performing the definite properties
     // analysis, as baseline has not been used to warm the caller up yet.
     if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) {
         RootedScript script(analysisContext, target->getOrCreateScript(analysisContext));
         if (!script)
@@ -4467,25 +4473,69 @@ IonBuilder::patchInlinedReturn(CallInfo 
             // Known non-object return: force |this|.
             rdef = callInfo.thisArg();
         }
     } else if (callInfo.isSetter()) {
         // Setters return their argument, not whatever value is returned.
         rdef = callInfo.getArg(0);
     }
 
+    if (!callInfo.isSetter())
+        rdef = specializeInlinedReturn(rdef, exit);
+
     MGoto *replacement = MGoto::New(alloc(), bottom);
     exit->end(replacement);
     if (!bottom->addPredecessorWithoutPhis(exit))
         return nullptr;
 
     return rdef;
 }
 
 MDefinition *
+IonBuilder::specializeInlinedReturn(MDefinition *rdef, MBasicBlock *exit)
+{
+    // Remove types from the return definition that weren't observed.
+    types::TemporaryTypeSet *types = bytecodeTypes(pc);
+
+    // The observed typeset doesn't contain extra information.
+    if (types->empty() || types->unknown())
+        return rdef;
+
+    // Decide if specializing is needed using the result typeset if available,
+    // else use the result type.
+
+    if (rdef->resultTypeSet()) {
+        // Don't specialize if return typeset is a subset of the
+        // observed typeset. The return typeset is already more specific.
+        if (rdef->resultTypeSet()->isSubset(types))
+            return rdef;
+    } else {
+        // Don't specialize if types are inaccordance, except for MIRType_Value
+        // and MIRType_Object (when not unknown object), since the typeset
+        // contains more specific information.
+        MIRType observedType = types->getKnownMIRType();
+        if (observedType == rdef->type() &&
+            observedType != MIRType_Value &&
+            (observedType != MIRType_Object || types->unknownObject()))
+        {
+            return rdef;
+        }
+    }
+
+    setCurrent(exit);
+
+    MTypeBarrier *barrier = nullptr;
+    rdef = addTypeBarrier(rdef, types, BarrierKind::TypeSet, &barrier);
+    if (barrier)
+        barrier->setNotMovable();
+
+    return rdef;
+}
+
+MDefinition *
 IonBuilder::patchInlinedReturns(CallInfo &callInfo, MIRGraphReturns &returns, MBasicBlock *bottom)
 {
     // Replaces MReturns with MGotos, returning the MDefinition
     // representing the return value, or nullptr.
     MOZ_ASSERT(returns.length() > 0);
 
     if (returns.length() == 1)
         return patchInlinedReturn(callInfo, returns[0], bottom);
@@ -6813,65 +6863,70 @@ IonBuilder::testSingletonPropertyTypes(M
 
     JSObject *proto = GetBuiltinPrototypePure(&script()->global(), key);
     if (proto)
         return testSingletonProperty(proto, name) == singleton;
 
     return false;
 }
 
-// Given an observed type set, annotates the IR as much as possible:
-// (1) If no type information is provided, the value on the top of the stack is
-//     left in place.
-// (2) If a single type definitely exists, and no type barrier is needed,
-//     then an infallible unbox instruction replaces the value on the top of
-//     the stack.
-// (3) If a type barrier is needed, but has an unknown type set, leave the
-//     value at the top of the stack.
-// (4) If a type barrier is needed, and has a single type, an unbox
-//     instruction replaces the top of the stack.
-// (5) Lastly, a type barrier instruction replaces the top of the stack.
 bool
 IonBuilder::pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, BarrierKind kind)
 {
+    MOZ_ASSERT(def == current->peek(-1));
+
+    MDefinition *replace = addTypeBarrier(current->pop(), observed, kind);
+    if (!replace)
+        return false;
+
+    current->push(replace);
+    return true;
+}
+
+// Given an observed type set, annotates the IR as much as possible:
+// (1) If no type information is provided, the given value is returned.
+// (2) If a single type definitely exists, and no type barrier is needed,
+//     then an infallible unbox instruction is returned.
+// (3) If a type barrier is needed, but has an unknown type set, the given
+//     value is returned.
+// (4) Lastly, a type barrier instruction is added and returned.
+MDefinition *
+IonBuilder::addTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, BarrierKind kind,
+                           MTypeBarrier **pbarrier)
+{
     // Barriers are never needed for instructions whose result will not be used.
     if (BytecodeIsPopped(pc))
-        return true;
+        return def;
 
     // If the instruction has no side effects, we'll resume the entire operation.
     // The actual type barrier will occur in the interpreter. If the
     // instruction is effectful, even if it has a singleton type, there
     // must be a resume point capturing the original def, and resuming
     // to that point will explicitly monitor the new type.
-
     if (kind == BarrierKind::NoBarrier) {
         MDefinition *replace = ensureDefiniteType(def, observed->getKnownMIRType());
-        if (replace != def) {
-            current->pop();
-            current->push(replace);
-        }
         replace->setResultTypeSet(observed);
-        return true;
+        return replace;
     }
 
     if (observed->unknown())
-        return true;
-
-    current->pop();
-
-    MInstruction *barrier = MTypeBarrier::New(alloc(), def, observed, kind);
+        return def;
+
+    MTypeBarrier *barrier = MTypeBarrier::New(alloc(), def, observed, kind);
     current->add(barrier);
 
+    if (pbarrier)
+        *pbarrier = barrier;
+
     if (barrier->type() == MIRType_Undefined)
-        return pushConstant(UndefinedValue());
+        return constant(UndefinedValue());
     if (barrier->type() == MIRType_Null)
-        return pushConstant(NullValue());
-
-    current->push(barrier);
-    return true;
+        return constant(NullValue());
+
+    return barrier;
 }
 
 bool
 IonBuilder::pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed, JSFunction* func)
 {
     MOZ_ASSERT(func && func->isNative() && func->jitInfo());
 
     const JSJitInfo *jitinfo = func->jitInfo();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -353,16 +353,18 @@ class IonBuilder
     bool improveTypesAtTest(MDefinition *ins, bool trueBranch, MTest *test);
     bool improveTypesAtCompare(MCompare *ins, bool trueBranch, MTest *test);
     // Used to detect triangular structure at test.
     bool detectAndOrStructure(MPhi *ins, bool *branchIsTrue);
     bool replaceTypeSet(MDefinition *subject, types::TemporaryTypeSet *type, MTest *test);
 
     // Add a guard which ensure that the set of type which goes through this
     // generated code correspond to the observed types for the bytecode.
+    MDefinition *addTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed,
+                                BarrierKind kind, MTypeBarrier **pbarrier = nullptr);
     bool pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, BarrierKind kind);
 
     // As pushTypeBarrier, but will compute the needBarrier boolean itself based
     // on observed and the JSFunction that we're planning to call. The
     // JSFunction must be a DOM method or getter.
     bool pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed, JSFunction* func);
 
     // If definiteType is not known or def already has the right type, just
@@ -836,16 +838,17 @@ class IonBuilder
 
     MDefinition *makeCallsiteClone(JSFunction *target, MDefinition *fun);
     MCall *makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite);
     bool makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite);
 
     MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
     MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphReturns &returns,
                                      MBasicBlock *bottom);
+    MDefinition *specializeInlinedReturn(MDefinition *rdef, MBasicBlock *exit);
 
     bool objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
                                     bool isGetter, JSObject *foundProto, bool *guardGlobal);
     void freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
                                             JSObject *foundProto, bool allowEmptyTypesForGlobal = false);
     /*
      * Callers must pass a non-null globalGuard if they pass a non-null globalShape.
      */
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -14,16 +14,17 @@
 #include "jsinfer.h"
 #include "jstypes.h"
 
 #include "gc/Heap.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/IonTypes.h"
 #include "js/UbiNode.h"
+#include "vm/TraceLogging.h"
 
 namespace js {
 
 class AsmJSModule;
 
 namespace jit {
 
 class MacroAssembler;
@@ -280,16 +281,19 @@ struct IonScript
 
     // The optimization level this script was compiled in.
     OptimizationLevel optimizationLevel_;
 
     // Number of times we tried to enter this script via OSR but failed due to
     // a LOOPENTRY pc other than osrPc_.
     uint32_t osrPcMismatchCounter_;
 
+    // The tracelogger event used to log the start/stop of this IonScript.
+    TraceLoggerEvent traceLoggerScriptEvent_;
+
     IonBuilder *pendingBuilder_;
 
   private:
     inline uint8_t *bottomBuffer() {
         return reinterpret_cast<uint8_t *>(this);
     }
     inline const uint8_t *bottomBuffer() const {
         return reinterpret_cast<const uint8_t *>(this);
@@ -457,16 +461,19 @@ struct IonScript
         hasSPSInstrumentation_ = true;
     }
     void clearHasSPSInstrumentation() {
         hasSPSInstrumentation_ = false;
     }
     bool hasSPSInstrumentation() const {
         return hasSPSInstrumentation_;
     }
+    void setTraceLoggerEvent(TraceLoggerEvent &event) {
+        traceLoggerScriptEvent_ = event;
+    }
     const uint8_t *snapshots() const {
         return reinterpret_cast<const uint8_t *>(this) + snapshots_;
     }
     size_t snapshotsListSize() const {
         return snapshotsListSize_;
     }
     size_t snapshotsRVATableSize() const {
         return snapshotsRVATableSize_;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -709,17 +709,17 @@ struct AutoDeleteDebugModeOSRInfo
     explicit AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); }
     ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); }
 };
 
 void
 HandleException(ResumeFromException *rfe)
 {
     JSContext *cx = GetJSContextFromJitCode();
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
 
     JitSpew(JitSpew_IonInvalidate, "handling exception");
 
     // Clear any Ion return override that's been set.
     // This may happen if a callVM function causes an invalidation (setting the
     // override), and then fails, bypassing the bailout handlers that would
@@ -776,18 +776,18 @@ HandleException(ResumeFromException *rfe
 
                 // When profiling, each frame popped needs a notification that
                 // the function has exited, so invoke the probe that a function
                 // is exiting.
 
                 JSScript *script = frames.script();
                 probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
                 if (!frames.more()) {
-                    TraceLogStopEvent(logger, TraceLogger::IonMonkey);
-                    TraceLogStopEvent(logger);
+                    TraceLogStopEvent(logger, TraceLogger_IonMonkey);
+                    TraceLogStopEvent(logger, TraceLogger_Scripts);
                     break;
                 }
                 ++frames;
             }
 
             if (invalidated)
                 ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
 
@@ -807,18 +807,18 @@ HandleException(ResumeFromException *rfe
             //
             // We cannot delete it immediately because of the call to
             // iter.baselineScriptAndPc below.
             AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame());
 
             if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
                 return;
 
-            TraceLogStopEvent(logger, TraceLogger::Baseline);
-            TraceLogStopEvent(logger);
+            TraceLogStopEvent(logger, TraceLogger_Baseline);
+            TraceLogStopEvent(logger, TraceLogger_Scripts);
 
             // Unwind profiler pseudo-stack
             JSScript *script = iter.script();
             probes::ExitScript(cx, script, script->functionNonDelazifying(),
                                iter.baselineFrame()->hasPushedSPSFrame());
             // After this point, any pushed SPS frame would have been popped if it needed
             // to be.  Unset the flag here so that if we call DebugEpilogue below,
             // it doesn't try to pop the SPS frame again.
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -294,22 +294,18 @@ class LSimdSwizzleF : public LSimdSwizzl
     {}
 };
 
 // Base class for both int32x4 and float32x4 shuffle instructions.
 class LSimdShuffle : public LInstructionHelper<1, 2, 1>
 {
   public:
     LIR_HEADER(SimdShuffle);
-    LSimdShuffle(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp)
-    {