Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 30 Dec 2014 15:48:13 -0800
changeset 247514 88037f94b7d77d58f73b9b58d7c1c6235a966ca9
parent 247403 69b64e65fbb203acd9e5fd931e13e0a9150ccb0d (current diff)
parent 247513 ef3f759cc0b4e07bb7e40f4b54d792a546faaf78 (diff)
child 247522 3f4164d111f6d662bb77ce2f925509231e5d26db
child 247555 edccc126caae2f70cfd6b3ed8487d90383908ec0
child 247561 3db2403368741d765c89cfba1278a1ce5d6c6bdd
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
88037f94b7d7 / 37.0a1 / 20141231030205 / files
nightly linux64
88037f94b7d7 / 37.0a1 / 20141231030205 / files
nightly mac
88037f94b7d7 / 37.0a1 / 20141231030205 / files
nightly win32
88037f94b7d7 / 37.0a1 / 20141231030205 / files
nightly win64
88037f94b7d7 / 37.0a1 / 20141231030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c 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/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/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)
-    {
-        setOperand(0, lhs);
-        setOperand(1, rhs);
-        setTemp(0, temp);
-    }
+    LSimdShuffle()
+    {}
 
     const LAllocation *lhs() {
         return getOperand(0);
     }
     const LAllocation *rhs() {
         return getOperand(1);
     }
     const LDefinition *temp() {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3961,24 +3961,22 @@ LIRGenerator::visitSimdShuffle(MSimdShuf
     MOZ_ASSERT(IsSimdType(ins->rhs()->type()));
     MOZ_ASSERT(IsSimdType(ins->type()));
     MOZ_ASSERT(ins->type() == MIRType_Int32x4 || ins->type() == MIRType_Float32x4);
 
     bool zFromLHS = ins->laneZ() < 4;
     bool wFromLHS = ins->laneW() < 4;
     uint32_t lanesFromLHS = (ins->laneX() < 4) + (ins->laneY() < 4) + zFromLHS + wFromLHS;
 
-    LUse lhs = useRegisterAtStart(ins->lhs());
-    LUse rhs = useRegister(ins->rhs());
+    LSimdShuffle *lir = new (alloc()) LSimdShuffle();
+    lowerForFPU(lir, ins, ins->lhs(), ins->rhs());
 
     // See codegen for requirements details.
     LDefinition temp = (lanesFromLHS == 3) ? tempCopy(ins->rhs(), 1) : LDefinition::BogusTemp();
-
-    LSimdShuffle *lir = new (alloc()) LSimdShuffle(lhs, rhs, temp);
-    defineReuseInput(lir, ins, 0);
+    lir->setTemp(0, temp);
 }
 
 void
 LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith *ins)
 {
     MOZ_ASSERT(IsSimdType(ins->type()));
 
     // Cannot be at start, as the ouput is used as a temporary to store values.
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1664,126 +1664,115 @@ MacroAssembler::printf(const char *outpu
     passABIArg(value);
     callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf1_));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 #ifdef JS_TRACE_LOGGING
 void
-MacroAssembler::tracelogStart(Register logger, uint32_t textId)
+MacroAssembler::tracelogStartId(Register logger, uint32_t textId, bool force)
 {
-    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStartEvent;
+    if (!force && !TraceLogTextIdEnabled(textId))
+        return;
 
     PushRegsInMask(RegisterSet::Volatile());
 
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEventPrivate));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
-MacroAssembler::tracelogStart(Register logger, Register textId)
+MacroAssembler::tracelogStartId(Register logger, Register textId)
 {
-    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStartEvent;
-
     PushRegsInMask(RegisterSet::Volatile());
 
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     passABIArg(textId);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
-
-    regs.add(temp);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEventPrivate));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
-MacroAssembler::tracelogStop(Register logger, uint32_t textId)
+MacroAssembler::tracelogStartEvent(Register logger, Register event)
 {
-    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStopEvent;
+    void (&TraceLogFunc)(TraceLoggerThread *, const TraceLoggerEvent &) = TraceLogStartEvent;
+
+    PushRegsInMask(RegisterSet::Volatile());
+
+    RegisterSet regs = RegisterSet::Volatile();
+    regs.takeUnchecked(logger);
+    regs.takeUnchecked(event);
+
+    Register temp = regs.takeGeneral();
+
+    setupUnalignedABICall(2, temp);
+    passABIArg(logger);
+    passABIArg(event);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+
+    PopRegsInMask(RegisterSet::Volatile());
+}
+
+void
+MacroAssembler::tracelogStopId(Register logger, uint32_t textId, bool force)
+{
+    if (!force && !TraceLogTextIdEnabled(textId))
+        return;
 
     PushRegsInMask(RegisterSet::Volatile());
 
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
 
-    regs.add(temp);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEventPrivate));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
-MacroAssembler::tracelogStop(Register logger, Register textId)
+MacroAssembler::tracelogStopId(Register logger, Register textId)
 {
-#ifdef DEBUG
-    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStopEvent;
-
     PushRegsInMask(RegisterSet::Volatile());
-
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
+
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     passABIArg(textId);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
-
-    regs.add(temp);
-
-    PopRegsInMask(RegisterSet::Volatile());
-#else
-    tracelogStop(logger);
-#endif
-}
-
-void
-MacroAssembler::tracelogStop(Register logger)
-{
-    void (&TraceLogFunc)(TraceLogger*) = TraceLogStopEvent;
-
-    PushRegsInMask(RegisterSet::Volatile());
-
-    RegisterSet regs = RegisterSet::Volatile();
-    regs.takeUnchecked(logger);
-
-    Register temp = regs.takeGeneral();
-
-    setupUnalignedABICall(1, temp);
-    passABIArg(logger);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
-
-    regs.add(temp);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEventPrivate));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 #endif
 
 void
 MacroAssembler::convertInt32ValueToDouble(const Address &address, Register scratch, Label *done)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1202,21 +1202,21 @@ class MacroAssembler : public MacroAssem
 
     void finish();
 
     void assumeUnreachable(const char *output);
     void printf(const char *output);
     void printf(const char *output, Register value);
 
 #ifdef JS_TRACE_LOGGING
-    void tracelogStart(Register logger, uint32_t textId);
-    void tracelogStart(Register logger, Register textId);
-    void tracelogStop(Register logger, uint32_t textId);
-    void tracelogStop(Register logger, Register textId);
-    void tracelogStop(Register logger);
+    void tracelogStartId(Register logger, uint32_t textId, bool force = false);
+    void tracelogStartId(Register logger, Register textId);
+    void tracelogStartEvent(Register logger, Register event);
+    void tracelogStopId(Register logger, uint32_t textId, bool force = false);
+    void tracelogStopId(Register logger, Register textId);
 #endif
 
 #define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2)    \
     MOZ_ASSERT(IsFloatingPointType(type));                              \
     if (type == MIRType_Double)                                         \
         method##Double(arg1d, arg2);                                    \
     else                                                                \
         method##Float32(arg1f, arg2);                                   \
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -273,16 +273,18 @@ TypeBarrierPolicy::adjustInputs(TempAllo
         MOZ_ASSERT(!ins->hasDefUses());
         ins->setResultType(MIRType_Value);
         return true;
     }
 
     // Unbox / propagate the right type.
     MUnbox::Mode mode = MUnbox::TypeBarrier;
     MInstruction *replace = MUnbox::New(alloc, ins->getOperand(0), ins->type(), mode);
+    if (!ins->isMovable())
+        replace->setNotMovable();
 
     ins->block()->insertBefore(ins, replace);
     ins->replaceOperand(0, replace);
     if (!replace->typePolicy()->adjustInputs(alloc, replace))
         return false;
 
     // The TypeBarrier is equivalent to removing branches with unexpected
     // types.  The unexpected types would have changed Range Analysis
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -766,19 +766,19 @@ DebugPrologue(JSContext *cx, BaselineFra
 }
 
 bool
 DebugEpilogueOnBaselineReturn(JSContext *cx, BaselineFrame *frame, jsbytecode *pc)
 {
     if (!DebugEpilogue(cx, frame, pc, true)) {
         // DebugEpilogue popped the frame by updating jitTop, so run the stop event
         // here before we enter the exception handler.
-        TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-        TraceLogStopEvent(logger, TraceLogger::Baseline);
-        TraceLogStopEvent(logger); // Leave script.
+        TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+        TraceLogStopEvent(logger, TraceLogger_Baseline);
+        TraceLogStopEvent(logger, TraceLogger_Scripts);
         return false;
     }
 
     return true;
 }
 
 bool
 DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -54,20 +54,18 @@ CodeGeneratorARM::generatePrologue()
 bool
 CodeGeneratorARM::generateEpilogue()
 {
     MOZ_ASSERT(!gen->compilingAsmJS());
     masm.bind(&returnLabel_);
 
 #ifdef JS_TRACE_LOGGING
     if (gen->info().executionMode() == SequentialExecution) {
-        if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
-            return false;
-        if (!emitTracelogScriptStop())
-            return false;
+        emitTracelogStopEvent(TraceLogger_IonMonkey);
+        emitTracelogScriptStop();
     }
 #endif
 
     masm.freeStack(frameSize());
     MOZ_ASSERT(masm.framePushed() == 0);
     masm.pop(pc);
     masm.flushBuffer();
     return true;
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -52,20 +52,18 @@ CodeGeneratorMIPS::generatePrologue()
 bool
 CodeGeneratorMIPS::generateEpilogue()
 {
     MOZ_ASSERT(!gen->compilingAsmJS());
     masm.bind(&returnLabel_);
 
 #ifdef JS_TRACE_LOGGING
     if (gen->info().executionMode() == SequentialExecution) {
-        if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
-            return false;
-        if (!emitTracelogScriptStop())
-            return false;
+        emitTracelogStopEvent(TraceLogger::IonMonkey);
+        emitTracelogScriptStop();
     }
 #endif
 
     masm.freeStack(frameSize());
     MOZ_ASSERT(masm.framePushed() == 0);
     masm.ret();
     return true;
 }
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -2098,20 +2098,52 @@ class AssemblerX86Shared : public Assemb
     void vmovlhps(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vmovlhps_rr(src1.code(), src0.code(), dest.code());
     }
     void vunpcklps(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vunpcklps_rr(src1.code(), src0.code(), dest.code());
     }
+    void vunpcklps(const Operand &src1, FloatRegister src0, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        switch (src1.kind()) {
+          case Operand::FPREG:
+            masm.vunpcklps_rr(src1.fpu(), src0.code(), dest.code());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.vunpcklps_mr(src1.disp(), src1.base(), src0.code(), dest.code());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vunpcklps_mr(src1.address(), src0.code(), dest.code());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     void vunpckhps(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vunpckhps_rr(src1.code(), src0.code(), dest.code());
     }
+    void vunpckhps(const Operand &src1, FloatRegister src0, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        switch (src1.kind()) {
+          case Operand::FPREG:
+            masm.vunpckhps_rr(src1.fpu(), src0.code(), dest.code());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.vunpckhps_mr(src1.disp(), src1.base(), src0.code(), dest.code());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vunpckhps_mr(src1.address(), src0.code(), dest.code());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     void vshufps(uint32_t mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vshufps_irr(mask, src1.code(), src0.code(), dest.code());
     }
     void vshufps(uint32_t mask, const Operand &src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         switch (src1.kind()) {
           case Operand::FPREG:
@@ -2319,16 +2351,29 @@ class AssemblerX86Shared : public Assemb
         ret |= unsigned(sourceLane) << 6;
         MOZ_ASSERT(ret < 256);
         return ret;
     }
     void vinsertps(uint32_t mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE41());
         masm.vinsertps_irr(mask, src1.code(), src0.code(), dest.code());
     }
+    void vinsertps(uint32_t mask, const Operand &src1, FloatRegister src0, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE41());
+        switch (src1.kind()) {
+          case Operand::FPREG:
+            masm.vinsertps_irr(mask, src1.fpu(), src0.code(), dest.code());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.vinsertps_imr(mask, src1.disp(), src1.base(), src0.code(), dest.code());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     unsigned blendpsMask(bool x, bool y, bool z, bool w) {
         return x | (y << 1) | (z << 2) | (w << 3);
     }
     void vblendps(unsigned mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE41());
         masm.vblendps_irr(mask, src1.code(), src0.code(), dest.code());
     }
     void vblendps(unsigned mask, const Operand &src1, FloatRegister src0, FloatRegister dest) {
--- a/js/src/jit/shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/shared/BaseAssembler-x86-shared.h
@@ -25,17 +25,18 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jit_shared_BaseAssembler_x86_shared_h
 #define jit_shared_BaseAssembler_x86_shared_h
 
-#include <inttypes.h>
+#include "mozilla/IntegerPrintfMacros.h"
+
 #include <stdarg.h>
 
 #include "jit/shared/AssemblerBuffer-x86-shared.h"
 
 #include "js/Vector.h"
 
 namespace js {
 namespace jit {
@@ -2933,21 +2934,37 @@ public:
         twoByteOpSimdInt64("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
     }
 #endif
 
     void vunpcklps_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vunpcklps", VEX_PS, OP2_UNPCKLPS_VsdWsd, src1, src0, dst);
     }
+    void vunpcklps_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vunpcklps", VEX_PS, OP2_UNPCKLPS_VsdWsd, offset, base, src0, dst);
+    }
+    void vunpcklps_mr(const void *addr, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vunpcklps", VEX_PS, OP2_UNPCKLPS_VsdWsd, addr, src0, dst);
+    }
 
     void vunpckhps_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vunpckhps", VEX_PS, OP2_UNPCKHPS_VsdWsd, src1, src0, dst);
     }
+    void vunpckhps_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vunpckhps", VEX_PS, OP2_UNPCKHPS_VsdWsd, offset, base, src0, dst);
+    }
+    void vunpckhps_mr(const void *addr, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vunpckhps", VEX_PS, OP2_UNPCKHPS_VsdWsd, addr, src0, dst);
+    }
 
     void vpand_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vpand", VEX_PD, OP2_PANDDQ_VdqWdq, src1, src0, dst);
     }
     void vpand_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vpand", VEX_PD, OP2_PANDDQ_VdqWdq, offset, base, src0, dst);
@@ -3601,16 +3618,20 @@ public:
     {
         threeByteOpImmSimd("vroundss", VEX_PD, OP3_ROUNDSS_VsdWsd, ESCAPE_ROUNDSD, mode, src1, src0, dst);
     }
 
     void vinsertps_irr(uint32_t mask, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, mask, src1, src0, dst);
     }
+    void vinsertps_imr(uint32_t mask, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, mask, offset, base, src0, dst);
+    }
 
     void vpinsrd_irr(unsigned lane, RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         MOZ_ASSERT(lane < 4);
         threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_PINSRD, lane, src1, src0, dst);
     }
 
     void vpinsrd_imr(unsigned lane, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
--- a/js/src/jit/shared/BaselineCompiler-shared.cpp
+++ b/js/src/jit/shared/BaselineCompiler-shared.cpp
@@ -25,17 +25,20 @@ BaselineCompilerShared::BaselineCompiler
     analysis_(alloc, script),
     frame(script, masm),
     stubSpace_(),
     icEntries_(),
     pcMappingEntries_(),
     icLoadLabels_(),
     pushedBeforeCall_(0),
     inCall_(false),
-    spsPushToggleOffset_()
+    spsPushToggleOffset_(),
+    traceLoggerEnterToggleOffset_(),
+    traceLoggerExitToggleOffset_(),
+    traceLoggerScriptTextIdOffset_()
 { }
 
 bool
 BaselineCompilerShared::callVM(const VMFunction &fun, CallVMPhase phase)
 {
     JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
     if (!code)
         return false;
--- a/js/src/jit/shared/BaselineCompiler-shared.h
+++ b/js/src/jit/shared/BaselineCompiler-shared.h
@@ -63,16 +63,19 @@ class BaselineCompilerShared
         CodeOffsetLabel label;
     };
     js::Vector<ICLoadLabel, 16, SystemAllocPolicy> icLoadLabels_;
 
     uint32_t pushedBeforeCall_;
     mozilla::DebugOnly<bool> inCall_;
 
     CodeOffsetLabel spsPushToggleOffset_;
+    CodeOffsetLabel traceLoggerEnterToggleOffset_;
+    CodeOffsetLabel traceLoggerExitToggleOffset_;
+    CodeOffsetLabel traceLoggerScriptTextIdOffset_;
 
     BaselineCompilerShared(JSContext *cx, TempAllocator &alloc, JSScript *script);
 
     ICEntry *allocateICEntry(ICStub *stub, ICEntry::Kind kind) {
         if (!stub)
             return nullptr;
 
         // Create the entry and add it to the vector.
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1015,17 +1015,17 @@ CodeGeneratorShared::callVM(const VMFunc
     if (ins->mirRaw()) {
         MOZ_ASSERT(ins->mirRaw()->isInstruction());
         MInstruction *mir = ins->mirRaw()->toInstruction();
         MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint());
     }
 #endif
 
 #ifdef JS_TRACE_LOGGING
-    emitTracelogStartEvent(TraceLogger::VM);
+    emitTracelogStartEvent(TraceLogger_VM);
 #endif
 
     // Stack is:
     //    ... frame ...
     //    [args]
 #ifdef DEBUG
     MOZ_ASSERT(pushedArgs_ == fun.explicitArgs);
     pushedArgs_ = 0;
@@ -1060,17 +1060,17 @@ CodeGeneratorShared::callVM(const VMFunc
     int framePop = sizeof(ExitFrameLayout) - sizeof(void*);
 
     // Pop arguments from framePushed.
     masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop);
     // Stack is:
     //    ... frame ...
 
 #ifdef JS_TRACE_LOGGING
-    emitTracelogStopEvent(TraceLogger::VM);
+    emitTracelogStopEvent(TraceLogger_VM);
 #endif
 }
 
 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared>
 {
     FloatRegister src_;
     Register dest_;
     bool needFloat32Conversion_;
@@ -1393,83 +1393,76 @@ CodeGeneratorShared::computeDivisionCons
     rmc.shiftAmount = shift;
 
     return rmc;
 }
 
 
 #ifdef JS_TRACE_LOGGING
 
-bool
+void
 CodeGeneratorShared::emitTracelogScript(bool isStart)
 {
+    if (!TraceLogTextIdEnabled(TraceLogger_Scripts))
+        return;
+
     Label done;
 
     RegisterSet regs = RegisterSet::Volatile();
     Register logger = regs.takeGeneral();
     Register script = regs.takeGeneral();
 
     masm.Push(logger);
 
     CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger);
-    if (!patchableTraceLoggers_.append(patchLogger))
-        return false;
+    masm.propagateOOM(patchableTraceLoggers_.append(patchLogger));
 
-    Address enabledAddress(logger, TraceLogger::offsetOfEnabled());
+    Address enabledAddress(logger, TraceLoggerThread::offsetOfEnabled());
     masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
 
     masm.Push(script);
 
     CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script);
-    if (!patchableTLScripts_.append(patchScript))
-        return false;
+    masm.propagateOOM(patchableTLScripts_.append(patchScript));
 
     if (isStart)
-        masm.tracelogStart(logger, script);
+        masm.tracelogStartId(logger, script);
     else
-        masm.tracelogStop(logger, script);
+        masm.tracelogStopId(logger, script);
 
     masm.Pop(script);
 
     masm.bind(&done);
 
     masm.Pop(logger);
-    return true;
 }
 
-bool
+void
 CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId)
 {
     if (!TraceLogTextIdEnabled(textId))
-        return true;
+        return;
 
     Label done;
     RegisterSet regs = RegisterSet::Volatile();
     Register logger = regs.takeGeneral();
 
     masm.Push(logger);
 
     CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
-    if (!patchableTraceLoggers_.append(patchLocation))
-        return false;
+    masm.propagateOOM(patchableTraceLoggers_.append(patchLocation));
 
-    Address enabledAddress(logger, TraceLogger::offsetOfEnabled());
+    Address enabledAddress(logger, TraceLoggerThread::offsetOfEnabled());
     masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
 
-    if (isStart) {
-        masm.tracelogStart(logger, textId);
-    } else {
-#ifdef DEBUG
-        masm.tracelogStop(logger, textId);
-#else
-        masm.tracelogStop(logger);
-#endif
-    }
+    if (isStart)
+        masm.tracelogStartId(logger, textId);
+    else
+        masm.tracelogStopId(logger, textId);
 
     masm.bind(&done);
 
     masm.Pop(logger);
-    return true;
 }
 #endif
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -53,20 +53,18 @@ bool
 CodeGeneratorX86Shared::generateEpilogue()
 {
     MOZ_ASSERT(!gen->compilingAsmJS());
 
     masm.bind(&returnLabel_);
 
 #ifdef JS_TRACE_LOGGING
     if (gen->info().executionMode() == SequentialExecution) {
-        if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
-            return false;
-        if (!emitTracelogScriptStop())
-            return false;
+        emitTracelogStopEvent(TraceLogger_IonMonkey);
+        emitTracelogScriptStop();
     }
 #endif
 
     // Pop the stack we allocated at the start of the function.
     masm.freeStack(frameSize());
     MOZ_ASSERT(masm.framePushed() == 0);
 
     masm.ret();
@@ -2140,19 +2138,18 @@ CodeGeneratorX86Shared::visitSimdSplatX4
       case MIRType_Int32x4: {
         Register r = ToRegister(ins->getOperand(0));
         masm.vmovd(r, output);
         masm.vpshufd(0, output, output);
         break;
       }
       case MIRType_Float32x4: {
         FloatRegister r = ToFloatRegister(ins->getOperand(0));
-        if (r != output)
-            masm.moveFloat32x4(r, output);
-        masm.vshufps(0, output, output, output);
+        FloatRegister rCopy = masm.reusedInputFloat32x4(r, output);
+        masm.vshufps(0, rCopy, rCopy, output);
         break;
       }
       default:
         MOZ_CRASH("Unknown SIMD kind");
     }
 }
 
 void
@@ -2186,17 +2183,22 @@ CodeGeneratorX86Shared::visitSimdExtract
         if (input != output)
             masm.moveFloat32(input, output);
     } else if (lane == LaneZ) {
         masm.moveHighPairToLowPairFloat32(input, output);
     } else {
         uint32_t mask = MacroAssembler::ComputeShuffleMask(lane);
         masm.shuffleFloat32(mask, input, output);
     }
-    masm.canonicalizeFloat(output);
+    // NaNs contained within SIMD values are not enforced to be canonical, so
+    // when we extract an element into a "regular" scalar JS value, we have to
+    // canonicalize. In asm.js code, we can skip this, as asm.js only has to
+    // canonicalize NaNs at FFI boundaries.
+    if (!gen->compilingAsmJS())
+        masm.canonicalizeFloat(output);
 }
 
 void
 CodeGeneratorX86Shared::visitSimdInsertElementI(LSimdInsertElementI *ins)
 {
     FloatRegister vector = ToFloatRegister(ins->vector());
     Register value = ToRegister(ins->value());
     FloatRegister output = ToFloatRegister(ins->output());
@@ -2326,19 +2328,18 @@ CodeGeneratorX86Shared::visitSimdSwizzle
     uint32_t mask = MacroAssembler::ComputeShuffleMask(x, y, z, w);
     masm.shuffleFloat32(mask, input, output);
 }
 
 void
 CodeGeneratorX86Shared::visitSimdShuffle(LSimdShuffle *ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
-    FloatRegister rhs = ToFloatRegister(ins->rhs());
+    Operand rhs = ToOperand(ins->rhs());
     FloatRegister out = ToFloatRegister(ins->output());
-    MOZ_ASSERT(out == lhs); // define reuse input
 
     uint32_t x = ins->laneX();
     uint32_t y = ins->laneY();
     uint32